diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c0786b3c..127ac87b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.7.1" + ".": "4.0.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 48863a6e..10c939b2 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 118 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-410219ea680089f02bb55163c673919703f946c3d6ad7ff5d6f607121d5287d5.yml -openapi_spec_hash: 2b3eee95d3f6796c7a61dfddf694a59a -config_hash: 666d6bb4b564f0d9d431124b5d1a0665 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-49233088b5e73dbb96bf7af27be3d4547632e3db1c2b00f14184900613325bbc.yml +openapi_spec_hash: b34f14b141d5019244112901c5c7c2d8 +config_hash: 94e9ba08201c3d1ca46e093e6a0138fa diff --git a/CHANGELOG.md b/CHANGELOG.md index f857ee96..0b2d01fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +## 4.0.0 (2025-09-30) + +Full Changelog: [v3.7.1...v4.0.0](https://github.com/openai/openai-java/compare/v3.7.1...v4.0.0) + +### ⚠ BREAKING CHANGES + +* **api:** `ResponseFunctionToolCallOutputItem.output` and `ResponseCustomToolCallOutput.output` now return `string | Array` instead of `string` only. This may break existing callsites that assume `output` is always a string. + +### Features + +* **api:** Support images and files for function call outputs in responses, BatchUsage ([de7a6c1](https://github.com/openai/openai-java/commit/de7a6c18862af4f94ce7bbc3a3ae447520be5880)) + + +### Bug Fixes + +* **client:** remove duplicate outputAsJson function ([4718871](https://github.com/openai/openai-java/commit/4718871e7e281b0a75c914d013e3b644340d5c4b)) + ## 3.7.1 (2025-09-26) Full Changelog: [v3.7.0...v3.7.1](https://github.com/openai/openai-java/compare/v3.7.0...v3.7.1) diff --git a/README.md b/README.md index 10f5bfeb..9518af40 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ -[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/3.7.1) -[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/3.7.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/3.7.1) +[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/4.0.0) +[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/4.0.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/4.0.0) @@ -11,7 +11,7 @@ The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https:// -The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/3.7.1). +The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/4.0.0). @@ -24,7 +24,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor ### Gradle ```kotlin -implementation("com.openai:openai-java:3.7.1") +implementation("com.openai:openai-java:4.0.0") ``` ### Maven @@ -33,7 +33,7 @@ implementation("com.openai:openai-java:3.7.1") com.openai openai-java - 3.7.1 + 4.0.0 ``` @@ -1342,7 +1342,7 @@ If you're using Spring Boot, then you can use the SDK's [Spring Boot starter](ht #### Gradle ```kotlin -implementation("com.openai:openai-java-spring-boot-starter:3.7.1") +implementation("com.openai:openai-java-spring-boot-starter:4.0.0") ``` #### Maven @@ -1351,7 +1351,7 @@ implementation("com.openai:openai-java-spring-boot-starter:3.7.1") com.openai openai-java-spring-boot-starter - 3.7.1 + 4.0.0 ``` diff --git a/build.gradle.kts b/build.gradle.kts index 8f68fa29..56b2996d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -8,7 +8,7 @@ repositories { allprojects { group = "com.openai" - version = "3.7.1" // x-release-please-version + version = "4.0.0" // x-release-please-version } subprojects { diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt index 9bd82591..6b855904 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/Batch.kt @@ -41,8 +41,10 @@ private constructor( private val finalizingAt: JsonField, private val inProgressAt: JsonField, private val metadata: JsonField, + private val model: JsonField, private val outputFileId: JsonField, private val requestCounts: JsonField, + private val usage: JsonField, private val additionalProperties: MutableMap, ) { @@ -82,12 +84,14 @@ private constructor( @ExcludeMissing inProgressAt: JsonField = JsonMissing.of(), @JsonProperty("metadata") @ExcludeMissing metadata: JsonField = JsonMissing.of(), + @JsonProperty("model") @ExcludeMissing model: JsonField = JsonMissing.of(), @JsonProperty("output_file_id") @ExcludeMissing outputFileId: JsonField = JsonMissing.of(), @JsonProperty("request_counts") @ExcludeMissing requestCounts: JsonField = JsonMissing.of(), + @JsonProperty("usage") @ExcludeMissing usage: JsonField = JsonMissing.of(), ) : this( id, completionWindow, @@ -107,8 +111,10 @@ private constructor( finalizingAt, inProgressAt, metadata, + model, outputFileId, requestCounts, + usage, mutableMapOf(), ) @@ -262,6 +268,17 @@ private constructor( */ fun metadata(): Optional = metadata.getOptional("metadata") + /** + * Model ID used to process the batch, like `gpt-5-2025-08-07`. OpenAI offers a wide range of + * models with different capabilities, performance characteristics, and price points. Refer to + * the [model guide](https://platform.openai.com/docs/models) to browse and compare available + * models. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun model(): Optional = model.getOptional("model") + /** * The ID of the file containing the outputs of successfully executed requests. * @@ -278,6 +295,15 @@ private constructor( */ fun requestCounts(): Optional = requestCounts.getOptional("request_counts") + /** + * Represents token usage details including input tokens, output tokens, a breakdown of output + * tokens, and the total tokens used. Only populated on batches created after September 7, 2025. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun usage(): Optional = usage.getOptional("usage") + /** * Returns the raw JSON value of [id]. * @@ -410,6 +436,13 @@ private constructor( */ @JsonProperty("metadata") @ExcludeMissing fun _metadata(): JsonField = metadata + /** + * Returns the raw JSON value of [model]. + * + * Unlike [model], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("model") @ExcludeMissing fun _model(): JsonField = model + /** * Returns the raw JSON value of [outputFileId]. * @@ -428,6 +461,13 @@ private constructor( @ExcludeMissing fun _requestCounts(): JsonField = requestCounts + /** + * Returns the raw JSON value of [usage]. + * + * Unlike [usage], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("usage") @ExcludeMissing fun _usage(): JsonField = usage + @JsonAnySetter private fun putAdditionalProperty(key: String, value: JsonValue) { additionalProperties.put(key, value) @@ -479,8 +519,10 @@ private constructor( private var finalizingAt: JsonField = JsonMissing.of() private var inProgressAt: JsonField = JsonMissing.of() private var metadata: JsonField = JsonMissing.of() + private var model: JsonField = JsonMissing.of() private var outputFileId: JsonField = JsonMissing.of() private var requestCounts: JsonField = JsonMissing.of() + private var usage: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @JvmSynthetic @@ -503,8 +545,10 @@ private constructor( finalizingAt = batch.finalizingAt inProgressAt = batch.inProgressAt metadata = batch.metadata + model = batch.model outputFileId = batch.outputFileId requestCounts = batch.requestCounts + usage = batch.usage additionalProperties = batch.additionalProperties.toMutableMap() } @@ -729,6 +773,22 @@ private constructor( */ fun metadata(metadata: JsonField) = apply { this.metadata = metadata } + /** + * Model ID used to process the batch, like `gpt-5-2025-08-07`. OpenAI offers a wide range + * of models with different capabilities, performance characteristics, and price points. + * Refer to the [model guide](https://platform.openai.com/docs/models) to browse and compare + * available models. + */ + fun model(model: String) = model(JsonField.of(model)) + + /** + * Sets [Builder.model] to an arbitrary JSON value. + * + * You should usually call [Builder.model] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun model(model: JsonField) = apply { this.model = model } + /** The ID of the file containing the outputs of successfully executed requests. */ fun outputFileId(outputFileId: String) = outputFileId(JsonField.of(outputFileId)) @@ -758,6 +818,22 @@ private constructor( this.requestCounts = requestCounts } + /** + * Represents token usage details including input tokens, output tokens, a breakdown of + * output tokens, and the total tokens used. Only populated on batches created after + * September 7, 2025. + */ + fun usage(usage: BatchUsage) = usage(JsonField.of(usage)) + + /** + * Sets [Builder.usage] to an arbitrary JSON value. + * + * You should usually call [Builder.usage] with a well-typed [BatchUsage] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun usage(usage: JsonField) = apply { this.usage = usage } + fun additionalProperties(additionalProperties: Map) = apply { this.additionalProperties.clear() putAllAdditionalProperties(additionalProperties) @@ -814,8 +890,10 @@ private constructor( finalizingAt, inProgressAt, metadata, + model, outputFileId, requestCounts, + usage, additionalProperties.toMutableMap(), ) } @@ -849,8 +927,10 @@ private constructor( finalizingAt() inProgressAt() metadata().ifPresent { it.validate() } + model() outputFileId() requestCounts().ifPresent { it.validate() } + usage().ifPresent { it.validate() } validated = true } @@ -887,8 +967,10 @@ private constructor( (if (finalizingAt.asKnown().isPresent) 1 else 0) + (if (inProgressAt.asKnown().isPresent) 1 else 0) + (metadata.asKnown().getOrNull()?.validity() ?: 0) + + (if (model.asKnown().isPresent) 1 else 0) + (if (outputFileId.asKnown().isPresent) 1 else 0) + - (requestCounts.asKnown().getOrNull()?.validity() ?: 0) + (requestCounts.asKnown().getOrNull()?.validity() ?: 0) + + (usage.asKnown().getOrNull()?.validity() ?: 0) /** The current status of the batch. */ class Status @JsonCreator private constructor(private val value: JsonField) : Enum { @@ -1378,8 +1460,10 @@ private constructor( finalizingAt == other.finalizingAt && inProgressAt == other.inProgressAt && metadata == other.metadata && + model == other.model && outputFileId == other.outputFileId && requestCounts == other.requestCounts && + usage == other.usage && additionalProperties == other.additionalProperties } @@ -1403,8 +1487,10 @@ private constructor( finalizingAt, inProgressAt, metadata, + model, outputFileId, requestCounts, + usage, additionalProperties, ) } @@ -1412,5 +1498,5 @@ private constructor( override fun hashCode(): Int = hashCode override fun toString() = - "Batch{id=$id, completionWindow=$completionWindow, createdAt=$createdAt, endpoint=$endpoint, inputFileId=$inputFileId, object_=$object_, status=$status, cancelledAt=$cancelledAt, cancellingAt=$cancellingAt, completedAt=$completedAt, errorFileId=$errorFileId, errors=$errors, expiredAt=$expiredAt, expiresAt=$expiresAt, failedAt=$failedAt, finalizingAt=$finalizingAt, inProgressAt=$inProgressAt, metadata=$metadata, outputFileId=$outputFileId, requestCounts=$requestCounts, additionalProperties=$additionalProperties}" + "Batch{id=$id, completionWindow=$completionWindow, createdAt=$createdAt, endpoint=$endpoint, inputFileId=$inputFileId, object_=$object_, status=$status, cancelledAt=$cancelledAt, cancellingAt=$cancellingAt, completedAt=$completedAt, errorFileId=$errorFileId, errors=$errors, expiredAt=$expiredAt, expiresAt=$expiresAt, failedAt=$failedAt, finalizingAt=$finalizingAt, inProgressAt=$inProgressAt, metadata=$metadata, model=$model, outputFileId=$outputFileId, requestCounts=$requestCounts, usage=$usage, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchUsage.kt b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchUsage.kt new file mode 100644 index 00000000..28176d09 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/batches/BatchUsage.kt @@ -0,0 +1,712 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.batches + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import kotlin.jvm.optionals.getOrNull + +/** + * Represents token usage details including input tokens, output tokens, a breakdown of output + * tokens, and the total tokens used. Only populated on batches created after September 7, 2025. + */ +class BatchUsage +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val inputTokens: JsonField, + private val inputTokensDetails: JsonField, + private val outputTokens: JsonField, + private val outputTokensDetails: JsonField, + private val totalTokens: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("input_tokens") + @ExcludeMissing + inputTokens: JsonField = JsonMissing.of(), + @JsonProperty("input_tokens_details") + @ExcludeMissing + inputTokensDetails: JsonField = JsonMissing.of(), + @JsonProperty("output_tokens") + @ExcludeMissing + outputTokens: JsonField = JsonMissing.of(), + @JsonProperty("output_tokens_details") + @ExcludeMissing + outputTokensDetails: JsonField = JsonMissing.of(), + @JsonProperty("total_tokens") + @ExcludeMissing + totalTokens: JsonField = JsonMissing.of(), + ) : this( + inputTokens, + inputTokensDetails, + outputTokens, + outputTokensDetails, + totalTokens, + mutableMapOf(), + ) + + /** + * The number of input tokens. + * + * @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 inputTokens(): Long = inputTokens.getRequired("input_tokens") + + /** + * A detailed breakdown of the input tokens. + * + * @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 inputTokensDetails(): InputTokensDetails = + inputTokensDetails.getRequired("input_tokens_details") + + /** + * The number of output tokens. + * + * @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 outputTokens(): Long = outputTokens.getRequired("output_tokens") + + /** + * A detailed breakdown of the output tokens. + * + * @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 outputTokensDetails(): OutputTokensDetails = + outputTokensDetails.getRequired("output_tokens_details") + + /** + * The total number of tokens used. + * + * @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 totalTokens(): Long = totalTokens.getRequired("total_tokens") + + /** + * Returns the raw JSON value of [inputTokens]. + * + * Unlike [inputTokens], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("input_tokens") @ExcludeMissing fun _inputTokens(): JsonField = inputTokens + + /** + * Returns the raw JSON value of [inputTokensDetails]. + * + * Unlike [inputTokensDetails], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("input_tokens_details") + @ExcludeMissing + fun _inputTokensDetails(): JsonField = inputTokensDetails + + /** + * Returns the raw JSON value of [outputTokens]. + * + * Unlike [outputTokens], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("output_tokens") + @ExcludeMissing + fun _outputTokens(): JsonField = outputTokens + + /** + * Returns the raw JSON value of [outputTokensDetails]. + * + * Unlike [outputTokensDetails], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("output_tokens_details") + @ExcludeMissing + fun _outputTokensDetails(): JsonField = outputTokensDetails + + /** + * Returns the raw JSON value of [totalTokens]. + * + * Unlike [totalTokens], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("total_tokens") @ExcludeMissing fun _totalTokens(): JsonField = totalTokens + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [BatchUsage]. + * + * The following fields are required: + * ```java + * .inputTokens() + * .inputTokensDetails() + * .outputTokens() + * .outputTokensDetails() + * .totalTokens() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [BatchUsage]. */ + class Builder internal constructor() { + + private var inputTokens: JsonField? = null + private var inputTokensDetails: JsonField? = null + private var outputTokens: JsonField? = null + private var outputTokensDetails: JsonField? = null + private var totalTokens: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(batchUsage: BatchUsage) = apply { + inputTokens = batchUsage.inputTokens + inputTokensDetails = batchUsage.inputTokensDetails + outputTokens = batchUsage.outputTokens + outputTokensDetails = batchUsage.outputTokensDetails + totalTokens = batchUsage.totalTokens + additionalProperties = batchUsage.additionalProperties.toMutableMap() + } + + /** The number of input tokens. */ + fun inputTokens(inputTokens: Long) = inputTokens(JsonField.of(inputTokens)) + + /** + * Sets [Builder.inputTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.inputTokens] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun inputTokens(inputTokens: JsonField) = apply { this.inputTokens = inputTokens } + + /** A detailed breakdown of the input tokens. */ + fun inputTokensDetails(inputTokensDetails: InputTokensDetails) = + inputTokensDetails(JsonField.of(inputTokensDetails)) + + /** + * Sets [Builder.inputTokensDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.inputTokensDetails] with a well-typed + * [InputTokensDetails] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun inputTokensDetails(inputTokensDetails: JsonField) = apply { + this.inputTokensDetails = inputTokensDetails + } + + /** The number of output tokens. */ + fun outputTokens(outputTokens: Long) = outputTokens(JsonField.of(outputTokens)) + + /** + * Sets [Builder.outputTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.outputTokens] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun outputTokens(outputTokens: JsonField) = apply { this.outputTokens = outputTokens } + + /** A detailed breakdown of the output tokens. */ + fun outputTokensDetails(outputTokensDetails: OutputTokensDetails) = + outputTokensDetails(JsonField.of(outputTokensDetails)) + + /** + * Sets [Builder.outputTokensDetails] to an arbitrary JSON value. + * + * You should usually call [Builder.outputTokensDetails] with a well-typed + * [OutputTokensDetails] value instead. This method is primarily for setting the field to an + * undocumented or not yet supported value. + */ + fun outputTokensDetails(outputTokensDetails: JsonField) = apply { + this.outputTokensDetails = outputTokensDetails + } + + /** The total number of tokens used. */ + fun totalTokens(totalTokens: Long) = totalTokens(JsonField.of(totalTokens)) + + /** + * Sets [Builder.totalTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.totalTokens] with a well-typed [Long] value instead. + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun totalTokens(totalTokens: JsonField) = apply { this.totalTokens = totalTokens } + + 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 [BatchUsage]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .inputTokens() + * .inputTokensDetails() + * .outputTokens() + * .outputTokensDetails() + * .totalTokens() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): BatchUsage = + BatchUsage( + checkRequired("inputTokens", inputTokens), + checkRequired("inputTokensDetails", inputTokensDetails), + checkRequired("outputTokens", outputTokens), + checkRequired("outputTokensDetails", outputTokensDetails), + checkRequired("totalTokens", totalTokens), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): BatchUsage = apply { + if (validated) { + return@apply + } + + inputTokens() + inputTokensDetails().validate() + outputTokens() + outputTokensDetails().validate() + totalTokens() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + (if (inputTokens.asKnown().isPresent) 1 else 0) + + (inputTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (outputTokens.asKnown().isPresent) 1 else 0) + + (outputTokensDetails.asKnown().getOrNull()?.validity() ?: 0) + + (if (totalTokens.asKnown().isPresent) 1 else 0) + + /** A detailed breakdown of the input tokens. */ + class InputTokensDetails + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val cachedTokens: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("cached_tokens") + @ExcludeMissing + cachedTokens: JsonField = JsonMissing.of() + ) : this(cachedTokens, mutableMapOf()) + + /** + * The number of tokens that were retrieved from the cache. + * [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). + * + * @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 cachedTokens(): Long = cachedTokens.getRequired("cached_tokens") + + /** + * Returns the raw JSON value of [cachedTokens]. + * + * Unlike [cachedTokens], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("cached_tokens") + @ExcludeMissing + fun _cachedTokens(): JsonField = cachedTokens + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [InputTokensDetails]. + * + * The following fields are required: + * ```java + * .cachedTokens() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [InputTokensDetails]. */ + class Builder internal constructor() { + + private var cachedTokens: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(inputTokensDetails: InputTokensDetails) = apply { + cachedTokens = inputTokensDetails.cachedTokens + additionalProperties = inputTokensDetails.additionalProperties.toMutableMap() + } + + /** + * The number of tokens that were retrieved from the cache. + * [More on prompt caching](https://platform.openai.com/docs/guides/prompt-caching). + */ + fun cachedTokens(cachedTokens: Long) = cachedTokens(JsonField.of(cachedTokens)) + + /** + * Sets [Builder.cachedTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.cachedTokens] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun cachedTokens(cachedTokens: JsonField) = apply { + this.cachedTokens = cachedTokens + } + + 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 [InputTokensDetails]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .cachedTokens() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): InputTokensDetails = + InputTokensDetails( + checkRequired("cachedTokens", cachedTokens), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): InputTokensDetails = apply { + if (validated) { + return@apply + } + + cachedTokens() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = (if (cachedTokens.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is InputTokensDetails && + cachedTokens == other.cachedTokens && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(cachedTokens, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "InputTokensDetails{cachedTokens=$cachedTokens, additionalProperties=$additionalProperties}" + } + + /** A detailed breakdown of the output tokens. */ + class OutputTokensDetails + @JsonCreator(mode = JsonCreator.Mode.DISABLED) + private constructor( + private val reasoningTokens: JsonField, + private val additionalProperties: MutableMap, + ) { + + @JsonCreator + private constructor( + @JsonProperty("reasoning_tokens") + @ExcludeMissing + reasoningTokens: JsonField = JsonMissing.of() + ) : this(reasoningTokens, mutableMapOf()) + + /** + * The number of reasoning tokens. + * + * @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 reasoningTokens(): Long = reasoningTokens.getRequired("reasoning_tokens") + + /** + * Returns the raw JSON value of [reasoningTokens]. + * + * Unlike [reasoningTokens], this method doesn't throw if the JSON field has an unexpected + * type. + */ + @JsonProperty("reasoning_tokens") + @ExcludeMissing + fun _reasoningTokens(): JsonField = reasoningTokens + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [OutputTokensDetails]. + * + * The following fields are required: + * ```java + * .reasoningTokens() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [OutputTokensDetails]. */ + class Builder internal constructor() { + + private var reasoningTokens: JsonField? = null + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(outputTokensDetails: OutputTokensDetails) = apply { + reasoningTokens = outputTokensDetails.reasoningTokens + additionalProperties = outputTokensDetails.additionalProperties.toMutableMap() + } + + /** The number of reasoning tokens. */ + fun reasoningTokens(reasoningTokens: Long) = + reasoningTokens(JsonField.of(reasoningTokens)) + + /** + * Sets [Builder.reasoningTokens] to an arbitrary JSON value. + * + * You should usually call [Builder.reasoningTokens] with a well-typed [Long] value + * instead. This method is primarily for setting the field to an undocumented or not yet + * supported value. + */ + fun reasoningTokens(reasoningTokens: JsonField) = apply { + this.reasoningTokens = reasoningTokens + } + + 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 [OutputTokensDetails]. + * + * Further updates to this [Builder] will not mutate the returned instance. + * + * The following fields are required: + * ```java + * .reasoningTokens() + * ``` + * + * @throws IllegalStateException if any required field is unset. + */ + fun build(): OutputTokensDetails = + OutputTokensDetails( + checkRequired("reasoningTokens", reasoningTokens), + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): OutputTokensDetails = apply { + if (validated) { + return@apply + } + + reasoningTokens() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object + * recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = (if (reasoningTokens.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is OutputTokensDetails && + reasoningTokens == other.reasoningTokens && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { Objects.hash(reasoningTokens, additionalProperties) } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "OutputTokensDetails{reasoningTokens=$reasoningTokens, additionalProperties=$additionalProperties}" + } + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is BatchUsage && + inputTokens == other.inputTokens && + inputTokensDetails == other.inputTokensDetails && + outputTokens == other.outputTokens && + outputTokensDetails == other.outputTokensDetails && + totalTokens == other.totalTokens && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash( + inputTokens, + inputTokensDetails, + outputTokens, + outputTokensDetails, + totalTokens, + additionalProperties, + ) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "BatchUsage{inputTokens=$inputTokens, inputTokensDetails=$inputTokensDetails, outputTokens=$outputTokens, outputTokensDetails=$outputTokensDetails, totalTokens=$totalTokens, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCustomToolCallOutput.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCustomToolCallOutput.kt index 6997ce7f..61e0b166 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCustomToolCallOutput.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseCustomToolCallOutput.kt @@ -6,22 +6,35 @@ 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.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 /** The output of a custom tool call from your code, being sent back to the model. */ class ResponseCustomToolCallOutput @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val callId: JsonField, - private val output: JsonField, + private val output: JsonField, private val type: JsonValue, private val id: JsonField, private val additionalProperties: MutableMap, @@ -30,7 +43,7 @@ private constructor( @JsonCreator private constructor( @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), - @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), + @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), ) : this(callId, output, type, id, mutableMapOf()) @@ -44,12 +57,13 @@ private constructor( fun callId(): String = callId.getRequired("call_id") /** - * The output from the custom tool call generated by your code. + * The output from the custom tool call generated by your code. Can be a string or an list of + * output content. * * @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(): String = output.getRequired("output") + fun output(): Output = output.getRequired("output") /** * The type of the custom tool call output. Always `custom_tool_call_output`. @@ -84,7 +98,7 @@ private constructor( * * Unlike [output], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output + @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output /** * Returns the raw JSON value of [id]. @@ -123,7 +137,7 @@ private constructor( class Builder internal constructor() { private var callId: JsonField? = null - private var output: 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 additionalProperties: MutableMap = mutableMapOf() @@ -148,16 +162,26 @@ private constructor( */ fun callId(callId: JsonField) = apply { this.callId = callId } - /** The output from the custom tool call generated by your code. */ - fun output(output: String) = output(JsonField.of(output)) + /** + * The output from the custom tool call generated by your code. Can be a string or an list + * of output content. + */ + 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 [String] value instead. This + * 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 } + 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.ofContentList(contentList)`. */ + fun outputOfContentList(contentList: List) = + output(Output.ofContentList(contentList)) /** * Sets the field to an arbitrary JSON value. @@ -234,7 +258,7 @@ private constructor( } callId() - output() + output().validate() _type().let { if (it != JsonValue.from("custom_tool_call_output")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -260,10 +284,442 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (if (callId.asKnown().isPresent) 1 else 0) + - (if (output.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) + /** + * The output from the custom tool call generated by your code. Can be a string or an list of + * output content. + */ + @JsonDeserialize(using = Output.Deserializer::class) + @JsonSerialize(using = Output.Serializer::class) + class Output + private constructor( + private val string: String? = null, + private val contentList: List? = null, + private val _json: JsonValue? = null, + ) { + + /** A string of the output of the custom tool call. */ + fun string(): Optional = Optional.ofNullable(string) + + /** Text, image, or file output of the custom tool call. */ + fun contentList(): Optional> = + Optional.ofNullable(contentList) + + fun isString(): Boolean = string != null + + fun isContentList(): Boolean = contentList != null + + /** A string of the output of the custom tool call. */ + fun asString(): String = string.getOrThrow("string") + + /** Text, image, or file output of the custom tool call. */ + fun asContentList(): List = + contentList.getOrThrow("contentList") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + contentList != null -> visitor.visitContentList(contentList) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Output = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitContentList( + contentList: List + ) { + contentList.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 visitContentList( + contentList: List + ) = contentList.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 && contentList == other.contentList + } + + override fun hashCode(): Int = Objects.hash(string, contentList) + + override fun toString(): String = + when { + string != null -> "Output{string=$string}" + contentList != null -> "Output{contentList=$contentList}" + _json != null -> "Output{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Output") + } + + companion object { + + /** A string of the output of the custom tool call. */ + @JvmStatic fun ofString(string: String) = Output(string = string) + + /** Text, image, or file output of the custom tool call. */ + @JvmStatic + fun ofContentList(contentList: List) = + Output(contentList = contentList.toImmutable()) + } + + /** An interface that defines how to map each variant of [Output] to a value of type [T]. */ + interface Visitor { + + /** A string of the output of the custom tool call. */ + fun visitString(string: String): T + + /** Text, image, or file output of the custom tool call. */ + fun visitContentList(contentList: 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: $json") + } + } + + 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(contentList = it, _json = json) }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> 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.contentList != null -> generator.writeObject(value.contentList) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Output") + } + } + } + + /** A text input to the model. */ + @JsonDeserialize(using = FunctionAndCustomToolCallOutput.Deserializer::class) + @JsonSerialize(using = FunctionAndCustomToolCallOutput.Serializer::class) + class FunctionAndCustomToolCallOutput + private constructor( + private val inputText: ResponseInputText? = null, + private val inputImage: ResponseInputImage? = null, + private val inputFile: ResponseInputFile? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** A file input to the model. */ + fun inputFile(): Optional = Optional.ofNullable(inputFile) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isInputFile(): Boolean = inputFile != null + + /** A text input to the model. */ + fun asInputText(): ResponseInputText = inputText.getOrThrow("inputText") + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun asInputImage(): ResponseInputImage = inputImage.getOrThrow("inputImage") + + /** A file input to the model. */ + fun asInputFile(): ResponseInputFile = inputFile.getOrThrow("inputFile") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + inputFile != null -> visitor.visitInputFile(inputFile) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): FunctionAndCustomToolCallOutput = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: ResponseInputText) { + inputText.validate() + } + + override fun visitInputImage(inputImage: ResponseInputImage) { + inputImage.validate() + } + + override fun visitInputFile(inputFile: ResponseInputFile) { + inputFile.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: ResponseInputText) = + inputText.validity() + + override fun visitInputImage(inputImage: ResponseInputImage) = + inputImage.validity() + + override fun visitInputFile(inputFile: ResponseInputFile) = + inputFile.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FunctionAndCustomToolCallOutput && + inputText == other.inputText && + inputImage == other.inputImage && + inputFile == other.inputFile + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage, inputFile) + + override fun toString(): String = + when { + inputText != null -> "FunctionAndCustomToolCallOutput{inputText=$inputText}" + inputImage != null -> "FunctionAndCustomToolCallOutput{inputImage=$inputImage}" + inputFile != null -> "FunctionAndCustomToolCallOutput{inputFile=$inputFile}" + _json != null -> "FunctionAndCustomToolCallOutput{_unknown=$_json}" + else -> throw IllegalStateException("Invalid FunctionAndCustomToolCallOutput") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofInputText(inputText: ResponseInputText) = + FunctionAndCustomToolCallOutput(inputText = inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + @JvmStatic + fun ofInputImage(inputImage: ResponseInputImage) = + FunctionAndCustomToolCallOutput(inputImage = inputImage) + + /** A file input to the model. */ + @JvmStatic + fun ofInputFile(inputFile: ResponseInputFile) = + FunctionAndCustomToolCallOutput(inputFile = inputFile) + } + + /** + * An interface that defines how to map each variant of + * [FunctionAndCustomToolCallOutput] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitInputText(inputText: ResponseInputText): T + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun visitInputImage(inputImage: ResponseInputImage): T + + /** A file input to the model. */ + fun visitInputFile(inputFile: ResponseInputFile): T + + /** + * Maps an unknown variant of [FunctionAndCustomToolCallOutput] to a value of type + * [T]. + * + * An instance of [FunctionAndCustomToolCallOutput] 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 FunctionAndCustomToolCallOutput: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer( + FunctionAndCustomToolCallOutput::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): FunctionAndCustomToolCallOutput { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputText = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputImage = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + "input_file" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputFile = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + } + + return FunctionAndCustomToolCallOutput(_json = json) + } + } + + internal class Serializer : + BaseSerializer( + FunctionAndCustomToolCallOutput::class + ) { + + override fun serialize( + value: FunctionAndCustomToolCallOutput, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.inputFile != null -> generator.writeObject(value.inputFile) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid FunctionAndCustomToolCallOutput") + } + } + } + } + } + override fun equals(other: Any?): Boolean { if (this === other) { return true diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt index b32262eb..38108765 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEvent.kt @@ -21,6 +21,7 @@ class ResponseFunctionCallArgumentsDoneEvent private constructor( private val arguments: JsonField, private val itemId: JsonField, + private val name: JsonField, private val outputIndex: JsonField, private val sequenceNumber: JsonField, private val type: JsonValue, @@ -31,6 +32,7 @@ private constructor( private constructor( @JsonProperty("arguments") @ExcludeMissing arguments: JsonField = JsonMissing.of(), @JsonProperty("item_id") @ExcludeMissing itemId: JsonField = JsonMissing.of(), + @JsonProperty("name") @ExcludeMissing name: JsonField = JsonMissing.of(), @JsonProperty("output_index") @ExcludeMissing outputIndex: JsonField = JsonMissing.of(), @@ -38,7 +40,7 @@ private constructor( @ExcludeMissing sequenceNumber: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), - ) : this(arguments, itemId, outputIndex, sequenceNumber, type, mutableMapOf()) + ) : this(arguments, itemId, name, outputIndex, sequenceNumber, type, mutableMapOf()) /** * The function-call arguments. @@ -56,6 +58,14 @@ private constructor( */ fun itemId(): String = itemId.getRequired("item_id") + /** + * The name of the function that was called. + * + * @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") + /** * The index of the output item. * @@ -97,6 +107,13 @@ private constructor( */ @JsonProperty("item_id") @ExcludeMissing fun _itemId(): JsonField = itemId + /** + * 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 [outputIndex]. * @@ -135,6 +152,7 @@ private constructor( * ```java * .arguments() * .itemId() + * .name() * .outputIndex() * .sequenceNumber() * ``` @@ -147,6 +165,7 @@ private constructor( private var arguments: JsonField? = null private var itemId: JsonField? = null + private var name: JsonField? = null private var outputIndex: JsonField? = null private var sequenceNumber: JsonField? = null private var type: JsonValue = JsonValue.from("response.function_call_arguments.done") @@ -158,6 +177,7 @@ private constructor( ) = apply { arguments = responseFunctionCallArgumentsDoneEvent.arguments itemId = responseFunctionCallArgumentsDoneEvent.itemId + name = responseFunctionCallArgumentsDoneEvent.name outputIndex = responseFunctionCallArgumentsDoneEvent.outputIndex sequenceNumber = responseFunctionCallArgumentsDoneEvent.sequenceNumber type = responseFunctionCallArgumentsDoneEvent.type @@ -188,6 +208,17 @@ private constructor( */ fun itemId(itemId: JsonField) = apply { this.itemId = itemId } + /** The name of the function that was called. */ + 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 } + /** The index of the output item. */ fun outputIndex(outputIndex: Long) = outputIndex(JsonField.of(outputIndex)) @@ -256,6 +287,7 @@ private constructor( * ```java * .arguments() * .itemId() + * .name() * .outputIndex() * .sequenceNumber() * ``` @@ -266,6 +298,7 @@ private constructor( ResponseFunctionCallArgumentsDoneEvent( checkRequired("arguments", arguments), checkRequired("itemId", itemId), + checkRequired("name", name), checkRequired("outputIndex", outputIndex), checkRequired("sequenceNumber", sequenceNumber), type, @@ -282,6 +315,7 @@ private constructor( arguments() itemId() + name() outputIndex() sequenceNumber() _type().let { @@ -309,6 +343,7 @@ private constructor( internal fun validity(): Int = (if (arguments.asKnown().isPresent) 1 else 0) + (if (itemId.asKnown().isPresent) 1 else 0) + + (if (name.asKnown().isPresent) 1 else 0) + (if (outputIndex.asKnown().isPresent) 1 else 0) + (if (sequenceNumber.asKnown().isPresent) 1 else 0) + type.let { if (it == JsonValue.from("response.function_call_arguments.done")) 1 else 0 } @@ -321,6 +356,7 @@ private constructor( return other is ResponseFunctionCallArgumentsDoneEvent && arguments == other.arguments && itemId == other.itemId && + name == other.name && outputIndex == other.outputIndex && sequenceNumber == other.sequenceNumber && type == other.type && @@ -328,11 +364,19 @@ private constructor( } private val hashCode: Int by lazy { - Objects.hash(arguments, itemId, outputIndex, sequenceNumber, type, additionalProperties) + Objects.hash( + arguments, + itemId, + name, + outputIndex, + sequenceNumber, + type, + additionalProperties, + ) } override fun hashCode(): Int = hashCode override fun toString() = - "ResponseFunctionCallArgumentsDoneEvent{arguments=$arguments, itemId=$itemId, outputIndex=$outputIndex, sequenceNumber=$sequenceNumber, type=$type, additionalProperties=$additionalProperties}" + "ResponseFunctionCallArgumentsDoneEvent{arguments=$arguments, itemId=$itemId, name=$name, outputIndex=$outputIndex, sequenceNumber=$sequenceNumber, type=$type, additionalProperties=$additionalProperties}" } diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItem.kt new file mode 100644 index 00000000..4dbc6e8d --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItem.kt @@ -0,0 +1,249 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +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 input to the model. */ +@JsonDeserialize(using = ResponseFunctionCallOutputItem.Deserializer::class) +@JsonSerialize(using = ResponseFunctionCallOutputItem.Serializer::class) +class ResponseFunctionCallOutputItem +private constructor( + private val inputText: ResponseInputTextContent? = null, + private val inputImage: ResponseInputImageContent? = null, + private val inputFile: ResponseInputFileContent? = null, + private val _json: JsonValue? = null, +) { + + /** A text input to the model. */ + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision) + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** A file input to the model. */ + fun inputFile(): Optional = Optional.ofNullable(inputFile) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isInputFile(): Boolean = inputFile != null + + /** A text input to the model. */ + fun asInputText(): ResponseInputTextContent = inputText.getOrThrow("inputText") + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision) + */ + fun asInputImage(): ResponseInputImageContent = inputImage.getOrThrow("inputImage") + + /** A file input to the model. */ + fun asInputFile(): ResponseInputFileContent = inputFile.getOrThrow("inputFile") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + inputFile != null -> visitor.visitInputFile(inputFile) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): ResponseFunctionCallOutputItem = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: ResponseInputTextContent) { + inputText.validate() + } + + override fun visitInputImage(inputImage: ResponseInputImageContent) { + inputImage.validate() + } + + override fun visitInputFile(inputFile: ResponseInputFileContent) { + inputFile.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: ResponseInputTextContent) = + inputText.validity() + + override fun visitInputImage(inputImage: ResponseInputImageContent) = + inputImage.validity() + + override fun visitInputFile(inputFile: ResponseInputFileContent) = + inputFile.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ResponseFunctionCallOutputItem && + inputText == other.inputText && + inputImage == other.inputImage && + inputFile == other.inputFile + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage, inputFile) + + override fun toString(): String = + when { + inputText != null -> "ResponseFunctionCallOutputItem{inputText=$inputText}" + inputImage != null -> "ResponseFunctionCallOutputItem{inputImage=$inputImage}" + inputFile != null -> "ResponseFunctionCallOutputItem{inputFile=$inputFile}" + _json != null -> "ResponseFunctionCallOutputItem{_unknown=$_json}" + else -> throw IllegalStateException("Invalid ResponseFunctionCallOutputItem") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofInputText(inputText: ResponseInputTextContent) = + ResponseFunctionCallOutputItem(inputText = inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision) + */ + @JvmStatic + fun ofInputImage(inputImage: ResponseInputImageContent) = + ResponseFunctionCallOutputItem(inputImage = inputImage) + + /** A file input to the model. */ + @JvmStatic + fun ofInputFile(inputFile: ResponseInputFileContent) = + ResponseFunctionCallOutputItem(inputFile = inputFile) + } + + /** + * An interface that defines how to map each variant of [ResponseFunctionCallOutputItem] to a + * value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitInputText(inputText: ResponseInputTextContent): T + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision) + */ + fun visitInputImage(inputImage: ResponseInputImageContent): T + + /** A file input to the model. */ + fun visitInputFile(inputFile: ResponseInputFileContent): T + + /** + * Maps an unknown variant of [ResponseFunctionCallOutputItem] to a value of type [T]. + * + * An instance of [ResponseFunctionCallOutputItem] 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 ResponseFunctionCallOutputItem: $json") + } + } + + internal class Deserializer : + BaseDeserializer(ResponseFunctionCallOutputItem::class) { + + override fun ObjectCodec.deserialize(node: JsonNode): ResponseFunctionCallOutputItem { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFunctionCallOutputItem(inputText = it, _json = json) + } ?: ResponseFunctionCallOutputItem(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFunctionCallOutputItem(inputImage = it, _json = json) + } ?: ResponseFunctionCallOutputItem(_json = json) + } + "input_file" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + ResponseFunctionCallOutputItem(inputFile = it, _json = json) + } ?: ResponseFunctionCallOutputItem(_json = json) + } + } + + return ResponseFunctionCallOutputItem(_json = json) + } + } + + internal class Serializer : + BaseSerializer(ResponseFunctionCallOutputItem::class) { + + override fun serialize( + value: ResponseFunctionCallOutputItem, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.inputFile != null -> generator.writeObject(value.inputFile) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid ResponseFunctionCallOutputItem") + } + } + } +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt index ad3858a9..09c53a25 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItem.kt @@ -6,12 +6,24 @@ 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.getOrThrow +import com.openai.core.toImmutable import com.openai.errors.OpenAIInvalidDataException import java.util.Collections import java.util.Objects @@ -23,7 +35,7 @@ class ResponseFunctionToolCallOutputItem private constructor( private val id: JsonField, private val callId: JsonField, - private val output: JsonField, + private val output: JsonField, private val type: JsonValue, private val status: JsonField, private val additionalProperties: MutableMap, @@ -33,7 +45,7 @@ private constructor( private constructor( @JsonProperty("id") @ExcludeMissing id: JsonField = JsonMissing.of(), @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), - @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), + @JsonProperty("output") @ExcludeMissing output: JsonField = JsonMissing.of(), @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), @JsonProperty("status") @ExcludeMissing status: JsonField = JsonMissing.of(), ) : this(id, callId, output, type, status, mutableMapOf()) @@ -55,12 +67,13 @@ private constructor( fun callId(): String = callId.getRequired("call_id") /** - * A JSON string of the output of the function tool call. + * The output from the function call generated by your code. Can be a string or an list of + * output content. * * @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(): String = output.getRequired("output") + fun output(): Output = output.getRequired("output") /** * The type of the function tool call output. Always `function_call_output`. @@ -103,7 +116,7 @@ private constructor( * * Unlike [output], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output + @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output /** * Returns the raw JSON value of [status]. @@ -145,7 +158,7 @@ private constructor( private var id: JsonField? = null private var callId: JsonField? = null - private var output: JsonField? = null + private var output: JsonField? = null private var type: JsonValue = JsonValue.from("function_call_output") private var status: JsonField = JsonMissing.of() private var additionalProperties: MutableMap = mutableMapOf() @@ -184,16 +197,26 @@ private constructor( */ fun callId(callId: JsonField) = apply { this.callId = callId } - /** A JSON string of the output of the function tool call. */ - fun output(output: String) = output(JsonField.of(output)) + /** + * The output from the function call generated by your code. Can be a string or an list of + * output content. + */ + 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 [String] value instead. This + * 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 } + 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.ofContentList(contentList)`. */ + fun outputOfContentList(contentList: List) = + output(Output.ofContentList(contentList)) /** * Sets the field to an arbitrary JSON value. @@ -276,7 +299,7 @@ private constructor( id() callId() - output() + output().validate() _type().let { if (it != JsonValue.from("function_call_output")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -303,10 +326,442 @@ private constructor( internal fun validity(): Int = (if (id.asKnown().isPresent) 1 else 0) + (if (callId.asKnown().isPresent) 1 else 0) + - (if (output.asKnown().isPresent) 1 else 0) + + (output.asKnown().getOrNull()?.validity() ?: 0) + type.let { if (it == JsonValue.from("function_call_output")) 1 else 0 } + (status.asKnown().getOrNull()?.validity() ?: 0) + /** + * The output from the function call generated by your code. Can be a string or an list of + * output content. + */ + @JsonDeserialize(using = Output.Deserializer::class) + @JsonSerialize(using = Output.Serializer::class) + class Output + private constructor( + private val string: String? = null, + private val contentList: List? = null, + private val _json: JsonValue? = null, + ) { + + /** A string of the output of the function call. */ + fun string(): Optional = Optional.ofNullable(string) + + /** Text, image, or file output of the function call. */ + fun contentList(): Optional> = + Optional.ofNullable(contentList) + + fun isString(): Boolean = string != null + + fun isContentList(): Boolean = contentList != null + + /** A string of the output of the function call. */ + fun asString(): String = string.getOrThrow("string") + + /** Text, image, or file output of the function call. */ + fun asContentList(): List = + contentList.getOrThrow("contentList") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + contentList != null -> visitor.visitContentList(contentList) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Output = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitContentList( + contentList: List + ) { + contentList.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 visitContentList( + contentList: List + ) = contentList.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 && contentList == other.contentList + } + + override fun hashCode(): Int = Objects.hash(string, contentList) + + override fun toString(): String = + when { + string != null -> "Output{string=$string}" + contentList != null -> "Output{contentList=$contentList}" + _json != null -> "Output{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Output") + } + + companion object { + + /** A string of the output of the function call. */ + @JvmStatic fun ofString(string: String) = Output(string = string) + + /** Text, image, or file output of the function call. */ + @JvmStatic + fun ofContentList(contentList: List) = + Output(contentList = contentList.toImmutable()) + } + + /** An interface that defines how to map each variant of [Output] to a value of type [T]. */ + interface Visitor { + + /** A string of the output of the function call. */ + fun visitString(string: String): T + + /** Text, image, or file output of the function call. */ + fun visitContentList(contentList: 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: $json") + } + } + + 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(contentList = it, _json = json) }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible with + // all the possible variants (e.g. deserializing from object). + 0 -> 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.contentList != null -> generator.writeObject(value.contentList) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Output") + } + } + } + + /** A text input to the model. */ + @JsonDeserialize(using = FunctionAndCustomToolCallOutput.Deserializer::class) + @JsonSerialize(using = FunctionAndCustomToolCallOutput.Serializer::class) + class FunctionAndCustomToolCallOutput + private constructor( + private val inputText: ResponseInputText? = null, + private val inputImage: ResponseInputImage? = null, + private val inputFile: ResponseInputFile? = null, + private val _json: JsonValue? = null, + ) { + + /** A text input to the model. */ + fun inputText(): Optional = Optional.ofNullable(inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun inputImage(): Optional = Optional.ofNullable(inputImage) + + /** A file input to the model. */ + fun inputFile(): Optional = Optional.ofNullable(inputFile) + + fun isInputText(): Boolean = inputText != null + + fun isInputImage(): Boolean = inputImage != null + + fun isInputFile(): Boolean = inputFile != null + + /** A text input to the model. */ + fun asInputText(): ResponseInputText = inputText.getOrThrow("inputText") + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun asInputImage(): ResponseInputImage = inputImage.getOrThrow("inputImage") + + /** A file input to the model. */ + fun asInputFile(): ResponseInputFile = inputFile.getOrThrow("inputFile") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + inputText != null -> visitor.visitInputText(inputText) + inputImage != null -> visitor.visitInputImage(inputImage) + inputFile != null -> visitor.visitInputFile(inputFile) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): FunctionAndCustomToolCallOutput = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitInputText(inputText: ResponseInputText) { + inputText.validate() + } + + override fun visitInputImage(inputImage: ResponseInputImage) { + inputImage.validate() + } + + override fun visitInputFile(inputFile: ResponseInputFile) { + inputFile.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: ResponseInputText) = + inputText.validity() + + override fun visitInputImage(inputImage: ResponseInputImage) = + inputImage.validity() + + override fun visitInputFile(inputFile: ResponseInputFile) = + inputFile.validity() + + override fun unknown(json: JsonValue?) = 0 + } + ) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is FunctionAndCustomToolCallOutput && + inputText == other.inputText && + inputImage == other.inputImage && + inputFile == other.inputFile + } + + override fun hashCode(): Int = Objects.hash(inputText, inputImage, inputFile) + + override fun toString(): String = + when { + inputText != null -> "FunctionAndCustomToolCallOutput{inputText=$inputText}" + inputImage != null -> "FunctionAndCustomToolCallOutput{inputImage=$inputImage}" + inputFile != null -> "FunctionAndCustomToolCallOutput{inputFile=$inputFile}" + _json != null -> "FunctionAndCustomToolCallOutput{_unknown=$_json}" + else -> throw IllegalStateException("Invalid FunctionAndCustomToolCallOutput") + } + + companion object { + + /** A text input to the model. */ + @JvmStatic + fun ofInputText(inputText: ResponseInputText) = + FunctionAndCustomToolCallOutput(inputText = inputText) + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + @JvmStatic + fun ofInputImage(inputImage: ResponseInputImage) = + FunctionAndCustomToolCallOutput(inputImage = inputImage) + + /** A file input to the model. */ + @JvmStatic + fun ofInputFile(inputFile: ResponseInputFile) = + FunctionAndCustomToolCallOutput(inputFile = inputFile) + } + + /** + * An interface that defines how to map each variant of + * [FunctionAndCustomToolCallOutput] to a value of type [T]. + */ + interface Visitor { + + /** A text input to the model. */ + fun visitInputText(inputText: ResponseInputText): T + + /** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision). + */ + fun visitInputImage(inputImage: ResponseInputImage): T + + /** A file input to the model. */ + fun visitInputFile(inputFile: ResponseInputFile): T + + /** + * Maps an unknown variant of [FunctionAndCustomToolCallOutput] to a value of type + * [T]. + * + * An instance of [FunctionAndCustomToolCallOutput] 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 FunctionAndCustomToolCallOutput: $json" + ) + } + } + + internal class Deserializer : + BaseDeserializer( + FunctionAndCustomToolCallOutput::class + ) { + + override fun ObjectCodec.deserialize( + node: JsonNode + ): FunctionAndCustomToolCallOutput { + val json = JsonValue.fromJsonNode(node) + val type = json.asObject().getOrNull()?.get("type")?.asString()?.getOrNull() + + when (type) { + "input_text" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputText = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + "input_image" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputImage = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + "input_file" -> { + return tryDeserialize(node, jacksonTypeRef())?.let { + FunctionAndCustomToolCallOutput(inputFile = it, _json = json) + } ?: FunctionAndCustomToolCallOutput(_json = json) + } + } + + return FunctionAndCustomToolCallOutput(_json = json) + } + } + + internal class Serializer : + BaseSerializer( + FunctionAndCustomToolCallOutput::class + ) { + + override fun serialize( + value: FunctionAndCustomToolCallOutput, + generator: JsonGenerator, + provider: SerializerProvider, + ) { + when { + value.inputText != null -> generator.writeObject(value.inputText) + value.inputImage != null -> generator.writeObject(value.inputImage) + value.inputFile != null -> generator.writeObject(value.inputFile) + value._json != null -> generator.writeObject(value._json) + else -> + throw IllegalStateException("Invalid FunctionAndCustomToolCallOutput") + } + } + } + } + } + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated when * items are returned via API. diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFileContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFileContent.kt new file mode 100644 index 00000000..e3f5c3cd --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputFileContent.kt @@ -0,0 +1,318 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** A file input to the model. */ +class ResponseInputFileContent +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val type: JsonValue, + private val fileData: JsonField, + private val fileId: JsonField, + private val fileUrl: JsonField, + private val filename: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("file_data") @ExcludeMissing fileData: JsonField = JsonMissing.of(), + @JsonProperty("file_id") @ExcludeMissing fileId: JsonField = JsonMissing.of(), + @JsonProperty("file_url") @ExcludeMissing fileUrl: JsonField = JsonMissing.of(), + @JsonProperty("filename") @ExcludeMissing filename: JsonField = JsonMissing.of(), + ) : this(type, fileData, fileId, fileUrl, filename, mutableMapOf()) + + /** + * The type of the input item. Always `input_file`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_file") + * ``` + * + * 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 base64-encoded data of the file to be sent to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fileData(): Optional = fileData.getOptional("file_data") + + /** + * The ID of the file to be sent to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fileId(): Optional = fileId.getOptional("file_id") + + /** + * The URL of the file to be sent to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fileUrl(): Optional = fileUrl.getOptional("file_url") + + /** + * The name of the file to be sent to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun filename(): Optional = filename.getOptional("filename") + + /** + * Returns the raw JSON value of [fileData]. + * + * Unlike [fileData], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("file_data") @ExcludeMissing fun _fileData(): JsonField = fileData + + /** + * Returns the raw JSON value of [fileId]. + * + * Unlike [fileId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("file_id") @ExcludeMissing fun _fileId(): JsonField = fileId + + /** + * Returns the raw JSON value of [fileUrl]. + * + * Unlike [fileUrl], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("file_url") @ExcludeMissing fun _fileUrl(): JsonField = fileUrl + + /** + * Returns the raw JSON value of [filename]. + * + * Unlike [filename], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("filename") @ExcludeMissing fun _filename(): JsonField = filename + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** Returns a mutable builder for constructing an instance of [ResponseInputFileContent]. */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ResponseInputFileContent]. */ + class Builder internal constructor() { + + private var type: JsonValue = JsonValue.from("input_file") + private var fileData: JsonField = JsonMissing.of() + private var fileId: JsonField = JsonMissing.of() + private var fileUrl: JsonField = JsonMissing.of() + private var filename: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(responseInputFileContent: ResponseInputFileContent) = apply { + type = responseInputFileContent.type + fileData = responseInputFileContent.fileData + fileId = responseInputFileContent.fileId + fileUrl = responseInputFileContent.fileUrl + filename = responseInputFileContent.filename + additionalProperties = responseInputFileContent.additionalProperties.toMutableMap() + } + + /** + * 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_file") + * ``` + * + * 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 base64-encoded data of the file to be sent to the model. */ + fun fileData(fileData: String?) = fileData(JsonField.ofNullable(fileData)) + + /** Alias for calling [Builder.fileData] with `fileData.orElse(null)`. */ + fun fileData(fileData: Optional) = fileData(fileData.getOrNull()) + + /** + * Sets [Builder.fileData] to an arbitrary JSON value. + * + * You should usually call [Builder.fileData] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun fileData(fileData: JsonField) = apply { this.fileData = fileData } + + /** The ID of the file to be sent to the model. */ + fun fileId(fileId: String?) = fileId(JsonField.ofNullable(fileId)) + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) + + /** + * Sets [Builder.fileId] to an arbitrary JSON value. + * + * You should usually call [Builder.fileId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun fileId(fileId: JsonField) = apply { this.fileId = fileId } + + /** The URL of the file to be sent to the model. */ + fun fileUrl(fileUrl: String?) = fileUrl(JsonField.ofNullable(fileUrl)) + + /** Alias for calling [Builder.fileUrl] with `fileUrl.orElse(null)`. */ + fun fileUrl(fileUrl: Optional) = fileUrl(fileUrl.getOrNull()) + + /** + * Sets [Builder.fileUrl] to an arbitrary JSON value. + * + * You should usually call [Builder.fileUrl] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun fileUrl(fileUrl: JsonField) = apply { this.fileUrl = fileUrl } + + /** The name of the file to be sent to the model. */ + fun filename(filename: String?) = filename(JsonField.ofNullable(filename)) + + /** Alias for calling [Builder.filename] with `filename.orElse(null)`. */ + fun filename(filename: Optional) = filename(filename.getOrNull()) + + /** + * Sets [Builder.filename] to an arbitrary JSON value. + * + * You should usually call [Builder.filename] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun filename(filename: JsonField) = apply { this.filename = filename } + + 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 [ResponseInputFileContent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ResponseInputFileContent = + ResponseInputFileContent( + type, + fileData, + fileId, + fileUrl, + filename, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ResponseInputFileContent = apply { + if (validated) { + return@apply + } + + _type().let { + if (it != JsonValue.from("input_file")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + fileData() + fileId() + fileUrl() + filename() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + type.let { if (it == JsonValue.from("input_file")) 1 else 0 } + + (if (fileData.asKnown().isPresent) 1 else 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (fileUrl.asKnown().isPresent) 1 else 0) + + (if (filename.asKnown().isPresent) 1 else 0) + + override fun equals(other: Any?): Boolean { + if (this === other) { + return true + } + + return other is ResponseInputFileContent && + type == other.type && + fileData == other.fileData && + fileId == other.fileId && + fileUrl == other.fileUrl && + filename == other.filename && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(type, fileData, fileId, fileUrl, filename, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ResponseInputFileContent{type=$type, fileData=$fileData, fileId=$fileId, fileUrl=$fileUrl, filename=$filename, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImageContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImageContent.kt new file mode 100644 index 00000000..675e7734 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputImageContent.kt @@ -0,0 +1,430 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects +import java.util.Optional +import kotlin.jvm.optionals.getOrNull + +/** + * An image input to the model. Learn about + * [image inputs](https://platform.openai.com/docs/guides/vision) + */ +class ResponseInputImageContent +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val type: JsonValue, + private val detail: JsonField, + private val fileId: JsonField, + private val imageUrl: JsonField, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + @JsonProperty("detail") @ExcludeMissing detail: JsonField = JsonMissing.of(), + @JsonProperty("file_id") @ExcludeMissing fileId: JsonField = JsonMissing.of(), + @JsonProperty("image_url") @ExcludeMissing imageUrl: JsonField = JsonMissing.of(), + ) : this(type, detail, fileId, imageUrl, mutableMapOf()) + + /** + * The type of the input item. Always `input_image`. + * + * Expected to always return the following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * However, this method can be useful for debugging and logging (e.g. if the server responded + * with an unexpected value). + */ + @JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type + + /** + * The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. + * Defaults to `auto`. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun detail(): Optional = detail.getOptional("detail") + + /** + * The ID of the file to be sent to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun fileId(): Optional = fileId.getOptional("file_id") + + /** + * The URL of the image to be sent to the model. A fully qualified URL or base64 encoded image + * in a data URL. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the + * server responded with an unexpected value). + */ + fun imageUrl(): Optional = imageUrl.getOptional("image_url") + + /** + * 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 + + /** + * Returns the raw JSON value of [fileId]. + * + * Unlike [fileId], this method doesn't throw if the JSON field has an unexpected type. + */ + @JsonProperty("file_id") @ExcludeMissing fun _fileId(): JsonField = fileId + + /** + * 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 + + @JsonAnySetter + private fun putAdditionalProperty(key: String, value: JsonValue) { + additionalProperties.put(key, value) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ResponseInputImageContent]. + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ResponseInputImageContent]. */ + class Builder internal constructor() { + + private var type: JsonValue = JsonValue.from("input_image") + private var detail: JsonField = JsonMissing.of() + private var fileId: JsonField = JsonMissing.of() + private var imageUrl: JsonField = JsonMissing.of() + private var additionalProperties: MutableMap = mutableMapOf() + + @JvmSynthetic + internal fun from(responseInputImageContent: ResponseInputImageContent) = apply { + type = responseInputImageContent.type + detail = responseInputImageContent.detail + fileId = responseInputImageContent.fileId + imageUrl = responseInputImageContent.imageUrl + additionalProperties = responseInputImageContent.additionalProperties.toMutableMap() + } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("input_image") + * ``` + * + * This method is primarily for setting the field to an undocumented or not yet supported + * value. + */ + fun type(type: JsonValue) = apply { this.type = type } + + /** + * The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. + * Defaults to `auto`. + */ + fun detail(detail: 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 } + + /** The ID of the file to be sent to the model. */ + fun fileId(fileId: String?) = fileId(JsonField.ofNullable(fileId)) + + /** Alias for calling [Builder.fileId] with `fileId.orElse(null)`. */ + fun fileId(fileId: Optional) = fileId(fileId.getOrNull()) + + /** + * Sets [Builder.fileId] to an arbitrary JSON value. + * + * You should usually call [Builder.fileId] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun fileId(fileId: JsonField) = apply { this.fileId = fileId } + + /** + * The URL of the image to be sent to the model. A fully qualified URL or base64 encoded + * image in a data URL. + */ + fun imageUrl(imageUrl: String?) = imageUrl(JsonField.ofNullable(imageUrl)) + + /** Alias for calling [Builder.imageUrl] with `imageUrl.orElse(null)`. */ + fun imageUrl(imageUrl: Optional) = imageUrl(imageUrl.getOrNull()) + + /** + * 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 } + + 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 [ResponseInputImageContent]. + * + * Further updates to this [Builder] will not mutate the returned instance. + */ + fun build(): ResponseInputImageContent = + ResponseInputImageContent( + type, + detail, + fileId, + imageUrl, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ResponseInputImageContent = apply { + if (validated) { + return@apply + } + + _type().let { + if (it != JsonValue.from("input_image")) { + throw OpenAIInvalidDataException("'type' is invalid, received $it") + } + } + detail().ifPresent { it.validate() } + fileId() + imageUrl() + validated = true + } + + fun isValid(): Boolean = + try { + validate() + true + } catch (e: OpenAIInvalidDataException) { + false + } + + /** + * Returns a score indicating how many valid values are contained in this object recursively. + * + * Used for best match union deserialization. + */ + @JvmSynthetic + internal fun validity(): Int = + type.let { if (it == JsonValue.from("input_image")) 1 else 0 } + + (detail.asKnown().getOrNull()?.validity() ?: 0) + + (if (fileId.asKnown().isPresent) 1 else 0) + + (if (imageUrl.asKnown().isPresent) 1 else 0) + + /** + * The detail level of the image to be sent to the model. One of `high`, `low`, or `auto`. + * Defaults to `auto`. + */ + 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") + + @JvmStatic fun of(value: String) = Detail(JsonField.of(value)) + } + + /** An enum containing [Detail]'s known values. */ + enum class Known { + LOW, + HIGH, + AUTO, + } + + /** + * 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, + /** 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 + 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 + 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 + + 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 ResponseInputImageContent && + type == other.type && + detail == other.detail && + fileId == other.fileId && + imageUrl == other.imageUrl && + additionalProperties == other.additionalProperties + } + + private val hashCode: Int by lazy { + Objects.hash(type, detail, fileId, imageUrl, additionalProperties) + } + + override fun hashCode(): Int = hashCode + + override fun toString() = + "ResponseInputImageContent{type=$type, detail=$detail, fileId=$fileId, imageUrl=$imageUrl, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt index 47f57f09..6f917a4f 100644 --- a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputItem.kt @@ -2583,7 +2583,7 @@ private constructor( @JsonCreator(mode = JsonCreator.Mode.DISABLED) private constructor( private val callId: JsonField, - private val output: JsonField, + private val output: JsonField, private val type: JsonValue, private val id: JsonField, private val status: JsonField, @@ -2593,7 +2593,7 @@ private constructor( @JsonCreator private constructor( @JsonProperty("call_id") @ExcludeMissing callId: JsonField = JsonMissing.of(), - @JsonProperty("output") @ExcludeMissing output: 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(), @@ -2608,12 +2608,12 @@ private constructor( fun callId(): String = callId.getRequired("call_id") /** - * A JSON string of the output of the function tool call. + * Text, image, or file output of the function tool call. * * @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(): String = output.getRequired("output") + fun output(): Output = output.getRequired("output") /** * The type of the function tool call output. Always `function_call_output`. @@ -2658,7 +2658,7 @@ private constructor( * * Unlike [output], this method doesn't throw if the JSON field has an unexpected type. */ - @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output + @JsonProperty("output") @ExcludeMissing fun _output(): JsonField = output /** * Returns the raw JSON value of [id]. @@ -2704,7 +2704,7 @@ private constructor( class Builder internal constructor() { private var callId: JsonField? = null - private var output: 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() @@ -2732,8 +2732,20 @@ private constructor( */ fun callId(callId: JsonField) = apply { this.callId = callId } - /** A JSON string of the output of the function tool call. */ - fun output(output: String) = output(JsonField.of(output)) + /** Text, image, or file output of the function tool call. */ + 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)) /** * Sets the output to text representing the JSON serialized form of a given object. This @@ -2744,13 +2756,15 @@ private constructor( fun outputAsJson(functionResult: Any) = apply { output(toJsonString(functionResult)) } /** - * Sets [Builder.output] to an arbitrary JSON value. - * - * You should usually call [Builder.output] with a well-typed [String] value instead. - * This method is primarily for setting the field to an undocumented or not yet - * supported value. + * Alias for calling [output] with + * `Output.ofResponseFunctionCallOutputItemList(responseFunctionCallOutputItemList)`. */ - fun output(output: JsonField) = apply { this.output = output } + fun outputOfResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList: List + ) = + output( + Output.ofResponseFunctionCallOutputItemList(responseFunctionCallOutputItemList) + ) /** * Sets the field to an arbitrary JSON value. @@ -2853,7 +2867,7 @@ private constructor( } callId() - output() + output().validate() _type().let { if (it != JsonValue.from("function_call_output")) { throw OpenAIInvalidDataException("'type' is invalid, received $it") @@ -2881,11 +2895,219 @@ private constructor( @JvmSynthetic internal fun validity(): Int = (if (callId.asKnown().isPresent) 1 else 0) + - (if (output.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, image, or file output of the function tool call. */ + @JsonDeserialize(using = Output.Deserializer::class) + @JsonSerialize(using = Output.Serializer::class) + class Output + private constructor( + private val string: String? = null, + private val responseFunctionCallOutputItemList: List? = + null, + private val _json: JsonValue? = null, + ) { + + /** A JSON string of the output of the function tool call. */ + fun string(): Optional = Optional.ofNullable(string) + + fun responseFunctionCallOutputItemList(): + Optional> = + Optional.ofNullable(responseFunctionCallOutputItemList) + + fun isString(): Boolean = string != null + + fun isResponseFunctionCallOutputItemList(): Boolean = + responseFunctionCallOutputItemList != null + + /** A JSON string of the output of the function tool call. */ + fun asString(): String = string.getOrThrow("string") + + fun asResponseFunctionCallOutputItemList(): List = + responseFunctionCallOutputItemList.getOrThrow("responseFunctionCallOutputItemList") + + fun _json(): Optional = Optional.ofNullable(_json) + + fun accept(visitor: Visitor): T = + when { + string != null -> visitor.visitString(string) + responseFunctionCallOutputItemList != null -> + visitor.visitResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList + ) + else -> visitor.unknown(_json) + } + + private var validated: Boolean = false + + fun validate(): Output = apply { + if (validated) { + return@apply + } + + accept( + object : Visitor { + override fun visitString(string: String) {} + + override fun visitResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList: List + ) { + responseFunctionCallOutputItemList.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 visitResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList: List + ) = responseFunctionCallOutputItemList.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 && + responseFunctionCallOutputItemList == other.responseFunctionCallOutputItemList + } + + override fun hashCode(): Int = Objects.hash(string, responseFunctionCallOutputItemList) + + override fun toString(): String = + when { + string != null -> "Output{string=$string}" + responseFunctionCallOutputItemList != null -> + "Output{responseFunctionCallOutputItemList=$responseFunctionCallOutputItemList}" + _json != null -> "Output{_unknown=$_json}" + else -> throw IllegalStateException("Invalid Output") + } + + companion object { + + /** A JSON string of the output of the function tool call. */ + @JvmStatic fun ofString(string: String) = Output(string = string) + + @JvmStatic + fun ofResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList: List + ) = + Output( + responseFunctionCallOutputItemList = + responseFunctionCallOutputItemList.toImmutable() + ) + } + + /** + * An interface that defines how to map each variant of [Output] to a value of type [T]. + */ + interface Visitor { + + /** A JSON string of the output of the function tool call. */ + fun visitString(string: String): T + + fun visitResponseFunctionCallOutputItemList( + responseFunctionCallOutputItemList: 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: $json") + } + } + + 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( + responseFunctionCallOutputItemList = it, + _json = json, + ) + }, + ) + .filterNotNull() + .allMaxBy { it.validity() } + .toList() + return when (bestMatches.size) { + // This can happen if what we're deserializing is completely incompatible + // with all the possible variants (e.g. deserializing from object). + 0 -> 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.responseFunctionCallOutputItemList != null -> + generator.writeObject(value.responseFunctionCallOutputItemList) + value._json != null -> generator.writeObject(value._json) + else -> throw IllegalStateException("Invalid Output") + } + } + } + } + /** * The status of the item. One of `in_progress`, `completed`, or `incomplete`. Populated * when items are returned via API. diff --git a/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputTextContent.kt b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputTextContent.kt new file mode 100644 index 00000000..f5c52727 --- /dev/null +++ b/openai-java-core/src/main/kotlin/com/openai/models/responses/ResponseInputTextContent.kt @@ -0,0 +1,215 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +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.errors.OpenAIInvalidDataException +import java.util.Collections +import java.util.Objects + +/** A text input to the model. */ +class ResponseInputTextContent +@JsonCreator(mode = JsonCreator.Mode.DISABLED) +private constructor( + private val text: JsonField, + private val type: JsonValue, + private val additionalProperties: MutableMap, +) { + + @JsonCreator + private constructor( + @JsonProperty("text") @ExcludeMissing text: JsonField = JsonMissing.of(), + @JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(), + ) : this(text, type, mutableMapOf()) + + /** + * The text input to the model. + * + * @throws OpenAIInvalidDataException if the JSON field has an unexpected type or is + * unexpectedly missing or null (e.g. if the server responded with an unexpected value). + */ + fun text(): String = text.getRequired("text") + + /** + * The type of the input item. Always `input_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) + } + + @JsonAnyGetter + @ExcludeMissing + fun _additionalProperties(): Map = + Collections.unmodifiableMap(additionalProperties) + + fun toBuilder() = Builder().from(this) + + companion object { + + /** + * Returns a mutable builder for constructing an instance of [ResponseInputTextContent]. + * + * The following fields are required: + * ```java + * .text() + * ``` + */ + @JvmStatic fun builder() = Builder() + } + + /** A builder for [ResponseInputTextContent]. */ + 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(responseInputTextContent: ResponseInputTextContent) = apply { + text = responseInputTextContent.text + type = responseInputTextContent.type + additionalProperties = responseInputTextContent.additionalProperties.toMutableMap() + } + + /** The text input to the model. */ + fun text(text: String) = text(JsonField.of(text)) + + /** + * Sets [Builder.text] to an arbitrary JSON value. + * + * You should usually call [Builder.text] with a well-typed [String] value instead. This + * method is primarily for setting the field to an undocumented or not yet supported value. + */ + fun text(text: JsonField) = apply { this.text = text } + + /** + * Sets the field to an arbitrary JSON value. + * + * It is usually unnecessary to call this method because the field defaults to the + * following: + * ```java + * JsonValue.from("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 [ResponseInputTextContent]. + * + * 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(): ResponseInputTextContent = + ResponseInputTextContent( + checkRequired("text", text), + type, + additionalProperties.toMutableMap(), + ) + } + + private var validated: Boolean = false + + fun validate(): ResponseInputTextContent = 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 ResponseInputTextContent && + 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() = + "ResponseInputTextContent{text=$text, type=$type, additionalProperties=$additionalProperties}" +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchListPageResponseTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchListPageResponseTest.kt index 728f330a..eb32b576 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchListPageResponseTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchListPageResponseTest.kt @@ -49,10 +49,26 @@ internal class BatchListPageResponseTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .model("model") .outputFileId("output_file_id") .requestCounts( BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() ) + .usage( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) .build() ) .hasMore(true) @@ -96,10 +112,24 @@ internal class BatchListPageResponseTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .model("model") .outputFileId("output_file_id") .requestCounts( BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() ) + .usage( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + ) .build() ) assertThat(batchListPageResponse.hasMore()).isEqualTo(true) @@ -147,10 +177,26 @@ internal class BatchListPageResponseTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .model("model") .outputFileId("output_file_id") .requestCounts( BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() ) + .usage( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder() + .reasoningTokens(0L) + .build() + ) + .totalTokens(0L) + .build() + ) .build() ) .hasMore(true) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt index 98574995..c4a7229f 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchTest.kt @@ -47,10 +47,24 @@ internal class BatchTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .model("model") .outputFileId("output_file_id") .requestCounts( BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() ) + .usage( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + ) .build() assertThat(batch.id()).isEqualTo("id") @@ -88,9 +102,24 @@ internal class BatchTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + assertThat(batch.model()).contains("model") assertThat(batch.outputFileId()).contains("output_file_id") assertThat(batch.requestCounts()) .contains(BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build()) + assertThat(batch.usage()) + .contains( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + ) } @Test @@ -131,10 +160,24 @@ internal class BatchTest { .putAdditionalProperty("foo", JsonValue.from("string")) .build() ) + .model("model") .outputFileId("output_file_id") .requestCounts( BatchRequestCounts.builder().completed(0L).failed(0L).total(0L).build() ) + .usage( + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + ) .build() val roundtrippedBatch = diff --git a/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchUsageTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchUsageTest.kt new file mode 100644 index 00000000..c021e8bb --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/batches/BatchUsageTest.kt @@ -0,0 +1,60 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.batches + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class BatchUsageTest { + + @Test + fun create() { + val batchUsage = + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + + assertThat(batchUsage.inputTokens()).isEqualTo(0L) + assertThat(batchUsage.inputTokensDetails()) + .isEqualTo(BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build()) + assertThat(batchUsage.outputTokens()).isEqualTo(0L) + assertThat(batchUsage.outputTokensDetails()) + .isEqualTo(BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build()) + assertThat(batchUsage.totalTokens()).isEqualTo(0L) + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val batchUsage = + BatchUsage.builder() + .inputTokens(0L) + .inputTokensDetails( + BatchUsage.InputTokensDetails.builder().cachedTokens(0L).build() + ) + .outputTokens(0L) + .outputTokensDetails( + BatchUsage.OutputTokensDetails.builder().reasoningTokens(0L).build() + ) + .totalTokens(0L) + .build() + + val roundtrippedBatchUsage = + jsonMapper.readValue( + jsonMapper.writeValueAsString(batchUsage), + jacksonTypeRef(), + ) + + assertThat(roundtrippedBatchUsage).isEqualTo(batchUsage) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/conversations/items/ConversationItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/conversations/items/ConversationItemTest.kt index 9b8fee16..6d827f08 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/conversations/items/ConversationItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/conversations/items/ConversationItemTest.kt @@ -143,7 +143,7 @@ internal class ConversationItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() @@ -177,7 +177,7 @@ internal class ConversationItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() ) @@ -1086,7 +1086,7 @@ internal class ConversationItemTest { val customToolCallOutput = ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() @@ -1119,7 +1119,7 @@ internal class ConversationItemTest { ConversationItem.ofCustomToolCallOutput( ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCustomToolCallOutputTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCustomToolCallOutputTest.kt index ff792023..985d1b2c 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCustomToolCallOutputTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseCustomToolCallOutputTest.kt @@ -14,12 +14,13 @@ internal class ResponseCustomToolCallOutputTest { val responseCustomToolCallOutput = ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() assertThat(responseCustomToolCallOutput.callId()).isEqualTo("call_id") - assertThat(responseCustomToolCallOutput.output()).isEqualTo("output") + assertThat(responseCustomToolCallOutput.output()) + .isEqualTo(ResponseCustomToolCallOutput.Output.ofString("string")) assertThat(responseCustomToolCallOutput.id()).contains("id") } @@ -29,7 +30,7 @@ internal class ResponseCustomToolCallOutputTest { val responseCustomToolCallOutput = ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt index a2a492a1..7e64cf97 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallArgumentsDoneEventTest.kt @@ -15,12 +15,14 @@ internal class ResponseFunctionCallArgumentsDoneEventTest { ResponseFunctionCallArgumentsDoneEvent.builder() .arguments("arguments") .itemId("item_id") + .name("name") .outputIndex(0L) .sequenceNumber(0L) .build() assertThat(responseFunctionCallArgumentsDoneEvent.arguments()).isEqualTo("arguments") assertThat(responseFunctionCallArgumentsDoneEvent.itemId()).isEqualTo("item_id") + assertThat(responseFunctionCallArgumentsDoneEvent.name()).isEqualTo("name") assertThat(responseFunctionCallArgumentsDoneEvent.outputIndex()).isEqualTo(0L) assertThat(responseFunctionCallArgumentsDoneEvent.sequenceNumber()).isEqualTo(0L) } @@ -32,6 +34,7 @@ internal class ResponseFunctionCallArgumentsDoneEventTest { ResponseFunctionCallArgumentsDoneEvent.builder() .arguments("arguments") .itemId("item_id") + .name("name") .outputIndex(0L) .sequenceNumber(0L) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItemTest.kt new file mode 100644 index 00000000..582bf794 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionCallOutputItemTest.kt @@ -0,0 +1,143 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.JsonValue +import com.openai.core.jsonMapper +import com.openai.errors.OpenAIInvalidDataException +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +internal class ResponseFunctionCallOutputItemTest { + + @Test + fun ofInputText() { + val inputText = ResponseInputTextContent.builder().text("text").build() + + val responseFunctionCallOutputItem = ResponseFunctionCallOutputItem.ofInputText(inputText) + + assertThat(responseFunctionCallOutputItem.inputText()).contains(inputText) + assertThat(responseFunctionCallOutputItem.inputImage()).isEmpty + assertThat(responseFunctionCallOutputItem.inputFile()).isEmpty + } + + @Test + fun ofInputTextRoundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionCallOutputItem = + ResponseFunctionCallOutputItem.ofInputText( + ResponseInputTextContent.builder().text("text").build() + ) + + val roundtrippedResponseFunctionCallOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionCallOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionCallOutputItem) + .isEqualTo(responseFunctionCallOutputItem) + } + + @Test + fun ofInputImage() { + val inputImage = + ResponseInputImageContent.builder() + .detail(ResponseInputImageContent.Detail.LOW) + .fileId("file-123") + .imageUrl("image_url") + .build() + + val responseFunctionCallOutputItem = ResponseFunctionCallOutputItem.ofInputImage(inputImage) + + assertThat(responseFunctionCallOutputItem.inputText()).isEmpty + assertThat(responseFunctionCallOutputItem.inputImage()).contains(inputImage) + assertThat(responseFunctionCallOutputItem.inputFile()).isEmpty + } + + @Test + fun ofInputImageRoundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionCallOutputItem = + ResponseFunctionCallOutputItem.ofInputImage( + ResponseInputImageContent.builder() + .detail(ResponseInputImageContent.Detail.LOW) + .fileId("file-123") + .imageUrl("image_url") + .build() + ) + + val roundtrippedResponseFunctionCallOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionCallOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionCallOutputItem) + .isEqualTo(responseFunctionCallOutputItem) + } + + @Test + fun ofInputFile() { + val inputFile = + ResponseInputFileContent.builder() + .fileData("file_data") + .fileId("file-123") + .fileUrl("file_url") + .filename("filename") + .build() + + val responseFunctionCallOutputItem = ResponseFunctionCallOutputItem.ofInputFile(inputFile) + + assertThat(responseFunctionCallOutputItem.inputText()).isEmpty + assertThat(responseFunctionCallOutputItem.inputImage()).isEmpty + assertThat(responseFunctionCallOutputItem.inputFile()).contains(inputFile) + } + + @Test + fun ofInputFileRoundtrip() { + val jsonMapper = jsonMapper() + val responseFunctionCallOutputItem = + ResponseFunctionCallOutputItem.ofInputFile( + ResponseInputFileContent.builder() + .fileData("file_data") + .fileId("file-123") + .fileUrl("file_url") + .filename("filename") + .build() + ) + + val roundtrippedResponseFunctionCallOutputItem = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseFunctionCallOutputItem), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseFunctionCallOutputItem) + .isEqualTo(responseFunctionCallOutputItem) + } + + 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 responseFunctionCallOutputItem = + jsonMapper() + .convertValue(testCase.value, jacksonTypeRef()) + + val e = + assertThrows { responseFunctionCallOutputItem.validate() } + assertThat(e).hasMessageStartingWith("Unknown ") + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt index 09029632..0b50086d 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseFunctionToolCallOutputItemTest.kt @@ -15,13 +15,14 @@ internal class ResponseFunctionToolCallOutputItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() assertThat(responseFunctionToolCallOutputItem.id()).isEqualTo("id") assertThat(responseFunctionToolCallOutputItem.callId()).isEqualTo("call_id") - assertThat(responseFunctionToolCallOutputItem.output()).isEqualTo("output") + assertThat(responseFunctionToolCallOutputItem.output()) + .isEqualTo(ResponseFunctionToolCallOutputItem.Output.ofString("string")) assertThat(responseFunctionToolCallOutputItem.status()) .contains(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) } @@ -33,7 +34,7 @@ internal class ResponseFunctionToolCallOutputItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileContentTest.kt new file mode 100644 index 00000000..31922593 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputFileContentTest.kt @@ -0,0 +1,47 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ResponseInputFileContentTest { + + @Test + fun create() { + val responseInputFileContent = + ResponseInputFileContent.builder() + .fileData("file_data") + .fileId("file-123") + .fileUrl("file_url") + .filename("filename") + .build() + + assertThat(responseInputFileContent.fileData()).contains("file_data") + assertThat(responseInputFileContent.fileId()).contains("file-123") + assertThat(responseInputFileContent.fileUrl()).contains("file_url") + assertThat(responseInputFileContent.filename()).contains("filename") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputFileContent = + ResponseInputFileContent.builder() + .fileData("file_data") + .fileId("file-123") + .fileUrl("file_url") + .filename("filename") + .build() + + val roundtrippedResponseInputFileContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputFileContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputFileContent).isEqualTo(responseInputFileContent) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageContentTest.kt new file mode 100644 index 00000000..efca62a8 --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputImageContentTest.kt @@ -0,0 +1,45 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ResponseInputImageContentTest { + + @Test + fun create() { + val responseInputImageContent = + ResponseInputImageContent.builder() + .detail(ResponseInputImageContent.Detail.LOW) + .fileId("file-123") + .imageUrl("image_url") + .build() + + assertThat(responseInputImageContent.detail()) + .contains(ResponseInputImageContent.Detail.LOW) + assertThat(responseInputImageContent.fileId()).contains("file-123") + assertThat(responseInputImageContent.imageUrl()).contains("image_url") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputImageContent = + ResponseInputImageContent.builder() + .detail(ResponseInputImageContent.Detail.LOW) + .fileId("file-123") + .imageUrl("image_url") + .build() + + val roundtrippedResponseInputImageContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputImageContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputImageContent).isEqualTo(responseInputImageContent) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt index ffd484e4..d98f1a56 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputItemTest.kt @@ -615,7 +615,7 @@ internal class ResponseInputItemTest { val functionCallOutput = ResponseInputItem.FunctionCallOutput.builder() .callId("x") - .output("output") + .output("string") .id("fc_123") .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) .build() @@ -652,7 +652,7 @@ internal class ResponseInputItemTest { ResponseInputItem.ofFunctionCallOutput( ResponseInputItem.FunctionCallOutput.builder() .callId("x") - .output("output") + .output("string") .id("fc_123") .status(ResponseInputItem.FunctionCallOutput.Status.IN_PROGRESS) .build() @@ -1227,7 +1227,7 @@ internal class ResponseInputItemTest { val customToolCallOutput = ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() @@ -1263,7 +1263,7 @@ internal class ResponseInputItemTest { ResponseInputItem.ofCustomToolCallOutput( ResponseCustomToolCallOutput.builder() .callId("call_id") - .output("output") + .output("string") .id("id") .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextContentTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextContentTest.kt new file mode 100644 index 00000000..7650497f --- /dev/null +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseInputTextContentTest.kt @@ -0,0 +1,32 @@ +// File generated from our OpenAPI spec by Stainless. + +package com.openai.models.responses + +import com.fasterxml.jackson.module.kotlin.jacksonTypeRef +import com.openai.core.jsonMapper +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.Test + +internal class ResponseInputTextContentTest { + + @Test + fun create() { + val responseInputTextContent = ResponseInputTextContent.builder().text("text").build() + + assertThat(responseInputTextContent.text()).isEqualTo("text") + } + + @Test + fun roundtrip() { + val jsonMapper = jsonMapper() + val responseInputTextContent = ResponseInputTextContent.builder().text("text").build() + + val roundtrippedResponseInputTextContent = + jsonMapper.readValue( + jsonMapper.writeValueAsString(responseInputTextContent), + jacksonTypeRef(), + ) + + assertThat(roundtrippedResponseInputTextContent).isEqualTo(responseInputTextContent) + } +} diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt index 8892aee5..e76ada84 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseItemTest.kt @@ -528,7 +528,7 @@ internal class ResponseItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() @@ -560,7 +560,7 @@ internal class ResponseItemTest { ResponseFunctionToolCallOutputItem.builder() .id("id") .callId("call_id") - .output("output") + .output("string") .status(ResponseFunctionToolCallOutputItem.Status.IN_PROGRESS) .build() ) diff --git a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt index a6cd270f..0a2a864b 100644 --- a/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt +++ b/openai-java-core/src/test/kotlin/com/openai/models/responses/ResponseStreamEventTest.kt @@ -2185,6 +2185,7 @@ internal class ResponseStreamEventTest { ResponseFunctionCallArgumentsDoneEvent.builder() .arguments("arguments") .itemId("item_id") + .name("name") .outputIndex(0L) .sequenceNumber(0L) .build() @@ -2256,6 +2257,7 @@ internal class ResponseStreamEventTest { ResponseFunctionCallArgumentsDoneEvent.builder() .arguments("arguments") .itemId("item_id") + .name("name") .outputIndex(0L) .sequenceNumber(0L) .build()