Skip to content
Open
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "4.30.0"
".": "4.31.0"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 151
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-00994178cc8e20d71754b00c54b0e4f5b4128e1c1cce765e9b7d696bd8c80d33.yml
openapi_spec_hash: 81f404053b663f987209b4fb2d08a230
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-dd99495ad509338e6de862802839360dfe394d5cd6d6ba6d13fec8fca92328b8.yml
openapi_spec_hash: 68abda9122013a9ae3f084cfdbe8e8c1
config_hash: 5635033cdc8c930255f8b529a78de722
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
# Changelog

## 4.31.0 (2026-04-07)

Full Changelog: [v4.30.0...v4.31.0](https://github.com/openai/openai-java/compare/v4.30.0...v4.31.0)

### Features

* **api:** add phase field to conversations Message ([e562a17](https://github.com/openai/openai-java/commit/e562a1701cfb7c92aa308d80d7bfe8e99c6394c8))
* **api:** add WEB_SEARCH_CALL_RESULTS to ResponseIncludable enum ([eda0a61](https://github.com/openai/openai-java/commit/eda0a61f837af3a0aba2851a23320aadfce82979))


### Bug Fixes

* **api:** remove web_search_call.results from ResponseIncludable ([936b2ab](https://github.com/openai/openai-java/commit/936b2ab08e8641bd1d6a1b2432140762185e22eb))


### Chores

* **internal:** update multipart form array serialization ([240ca42](https://github.com/openai/openai-java/commit/240ca42401366b75cf70b0eb0ff9f63425c9b358))
* **tests:** bump steady to v0.20.1 ([a3c95b0](https://github.com/openai/openai-java/commit/a3c95b073d7b10a13ce83865947ab4fbec5a95d0))
* **tests:** bump steady to v0.20.2 ([78b1d56](https://github.com/openai/openai-java/commit/78b1d5629bf7d31173ec1cec50f3191959cdba9d))


### Documentation

* **api:** clarify file_batches usage in vector stores files and file batches ([9c56841](https://github.com/openai/openai-java/commit/9c56841ff2f052a7e54290b36c3f74528be3de19))
* fix function arguments typo in README ([#713](https://github.com/openai/openai-java/issues/713)) ([36c4888](https://github.com/openai/openai-java/commit/36c48883fa898da1b41ccc03fe1682934079923c))

## 4.30.0 (2026-03-25)

Full Changelog: [v4.29.1...v4.30.0](https://github.com/openai/openai-java/compare/v4.29.1...v4.30.0)
Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@

<!-- x-release-please-start-version -->

[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/4.30.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/4.30.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/4.30.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/4.31.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/4.31.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/4.31.0)

<!-- x-release-please-end -->

The OpenAI Java SDK provides convenient access to the [OpenAI REST API](https://platform.openai.com/docs) from applications written in Java.

<!-- x-release-please-start-version -->

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.30.0).
The REST API documentation can be found on [platform.openai.com](https://platform.openai.com/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.openai/openai-java/4.31.0).

<!-- x-release-please-end -->

Expand All @@ -24,7 +24,7 @@ The REST API documentation can be found on [platform.openai.com](https://platfor
### Gradle

```kotlin
implementation("com.openai:openai-java:4.30.0")
implementation("com.openai:openai-java:4.31.0")
```

### Maven
Expand All @@ -33,7 +33,7 @@ implementation("com.openai:openai-java:4.30.0")
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java</artifactId>
<version>4.30.0</version>
<version>4.31.0</version>
</dependency>
```

Expand Down Expand Up @@ -795,7 +795,7 @@ static class GetSdkScore {
```

When your functions are defined, add them to the input parameters using `addTool(Class<T>)` and then
call them if requested to do so in the AI model's response. `Function.argments(Class<T>)` can be
call them if requested to do so in the AI model's response. `Function.arguments(Class<T>)` can be
used to parse a function's parameters in JSON form to an instance of your function-defining class.
The fields of that instance will be set to the values of the parameters to the function call.

Expand Down Expand Up @@ -1342,7 +1342,7 @@ If you're using Spring Boot, then you can use the SDK's [Spring Boot starter](ht
#### Gradle

```kotlin
implementation("com.openai:openai-java-spring-boot-starter:4.30.0")
implementation("com.openai:openai-java-spring-boot-starter:4.31.0")
```

#### Maven
Expand All @@ -1351,7 +1351,7 @@ implementation("com.openai:openai-java-spring-boot-starter:4.30.0")
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-spring-boot-starter</artifactId>
<version>4.30.0</version>
<version>4.31.0</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repositories {

allprojects {
group = "com.openai"
version = "4.30.0" // x-release-please-version
version = "4.31.0" // x-release-please-version
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ private constructor(
private val role: JsonField<Role>,
private val status: JsonField<Status>,
private val type: JsonValue,
private val phase: JsonField<Phase>,
private val additionalProperties: MutableMap<String, JsonValue>,
) {

Expand All @@ -56,7 +57,8 @@ private constructor(
@JsonProperty("role") @ExcludeMissing role: JsonField<Role> = JsonMissing.of(),
@JsonProperty("status") @ExcludeMissing status: JsonField<Status> = JsonMissing.of(),
@JsonProperty("type") @ExcludeMissing type: JsonValue = JsonMissing.of(),
) : this(id, content, role, status, type, mutableMapOf())
@JsonProperty("phase") @ExcludeMissing phase: JsonField<Phase> = JsonMissing.of(),
) : this(id, content, role, status, type, phase, mutableMapOf())

/**
* The unique ID of the message.
Expand Down Expand Up @@ -105,6 +107,17 @@ private constructor(
*/
@JsonProperty("type") @ExcludeMissing fun _type(): JsonValue = type

/**
* Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer
* (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up
* requests, preserve and resend phase on all assistant messages — dropping it can degrade
* performance. Not used for user messages.
*
* @throws OpenAIInvalidDataException if the JSON field has an unexpected type (e.g. if the
* server responded with an unexpected value).
*/
fun phase(): Optional<Phase> = phase.getOptional("phase")

/**
* Returns the raw JSON value of [id].
*
Expand Down Expand Up @@ -133,6 +146,13 @@ private constructor(
*/
@JsonProperty("status") @ExcludeMissing fun _status(): JsonField<Status> = status

/**
* Returns the raw JSON value of [phase].
*
* Unlike [phase], this method doesn't throw if the JSON field has an unexpected type.
*/
@JsonProperty("phase") @ExcludeMissing fun _phase(): JsonField<Phase> = phase

@JsonAnySetter
private fun putAdditionalProperty(key: String, value: JsonValue) {
additionalProperties.put(key, value)
Expand Down Expand Up @@ -169,6 +189,7 @@ private constructor(
private var role: JsonField<Role>? = null
private var status: JsonField<Status>? = null
private var type: JsonValue = JsonValue.from("message")
private var phase: JsonField<Phase> = JsonMissing.of()
private var additionalProperties: MutableMap<String, JsonValue> = mutableMapOf()

@JvmSynthetic
Expand All @@ -178,6 +199,7 @@ private constructor(
role = message.role
status = message.status
type = message.type
phase = message.phase
additionalProperties = message.additionalProperties.toMutableMap()
}

Expand Down Expand Up @@ -359,6 +381,25 @@ private constructor(
*/
fun type(type: JsonValue) = apply { this.type = type }

/**
* Labels an `assistant` message as intermediate commentary (`commentary`) or the final
* answer (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending
* follow-up requests, preserve and resend phase on all assistant messages — dropping it can
* degrade performance. Not used for user messages.
*/
fun phase(phase: Phase?) = phase(JsonField.ofNullable(phase))

/** Alias for calling [Builder.phase] with `phase.orElse(null)`. */
fun phase(phase: Optional<Phase>) = phase(phase.getOrNull())

/**
* Sets [Builder.phase] to an arbitrary JSON value.
*
* You should usually call [Builder.phase] with a well-typed [Phase] value instead. This
* method is primarily for setting the field to an undocumented or not yet supported value.
*/
fun phase(phase: JsonField<Phase>) = apply { this.phase = phase }

fun additionalProperties(additionalProperties: Map<String, JsonValue>) = apply {
this.additionalProperties.clear()
putAllAdditionalProperties(additionalProperties)
Expand Down Expand Up @@ -400,6 +441,7 @@ private constructor(
checkRequired("role", role),
checkRequired("status", status),
type,
phase,
additionalProperties.toMutableMap(),
)
}
Expand All @@ -420,6 +462,7 @@ private constructor(
throw OpenAIInvalidDataException("'type' is invalid, received $it")
}
}
phase().ifPresent { it.validate() }
validated = true
}

Expand All @@ -442,7 +485,8 @@ private constructor(
(content.asKnown().getOrNull()?.sumOf { it.validity().toInt() } ?: 0) +
(role.asKnown().getOrNull()?.validity() ?: 0) +
(status.asKnown().getOrNull()?.validity() ?: 0) +
type.let { if (it == JsonValue.from("message")) 1 else 0 }
type.let { if (it == JsonValue.from("message")) 1 else 0 } +
(phase.asKnown().getOrNull()?.validity() ?: 0)

/** A content part that makes up an input or output item. */
@JsonDeserialize(using = Content.Deserializer::class)
Expand Down Expand Up @@ -1379,6 +1423,137 @@ private constructor(
override fun toString() = value.toString()
}

/**
* Labels an `assistant` message as intermediate commentary (`commentary`) or the final answer
* (`final_answer`). For models like `gpt-5.3-codex` and beyond, when sending follow-up
* requests, preserve and resend phase on all assistant messages — dropping it can degrade
* performance. Not used for user messages.
*/
class Phase @JsonCreator private constructor(private val value: JsonField<String>) : 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<String> = value

companion object {

@JvmField val COMMENTARY = of("commentary")

@JvmField val FINAL_ANSWER = of("final_answer")

@JvmStatic fun of(value: String) = Phase(JsonField.of(value))
}

/** An enum containing [Phase]'s known values. */
enum class Known {
COMMENTARY,
FINAL_ANSWER,
}

/**
* An enum containing [Phase]'s known values, as well as an [_UNKNOWN] member.
*
* An instance of [Phase] can contain an unknown value in a couple of cases:
* - It was deserialized from data that doesn't match any known member. For example, if the
* SDK is on an older version than the API, then the API may respond with new members that
* the SDK is unaware of.
* - It was constructed with an arbitrary value using the [of] method.
*/
enum class Value {
COMMENTARY,
FINAL_ANSWER,
/** An enum member indicating that [Phase] was instantiated with an unknown value. */
_UNKNOWN,
}

/**
* Returns an enum member corresponding to this class instance's value, or [Value._UNKNOWN]
* if the class was instantiated with an unknown value.
*
* Use the [known] method instead if you're certain the value is always known or if you want
* to throw for the unknown case.
*/
fun value(): Value =
when (this) {
COMMENTARY -> Value.COMMENTARY
FINAL_ANSWER -> Value.FINAL_ANSWER
else -> Value._UNKNOWN
}

/**
* Returns an enum member corresponding to this class instance's value.
*
* Use the [value] method instead if you're uncertain the value is always known and don't
* want to throw for the unknown case.
*
* @throws OpenAIInvalidDataException if this class instance's value is a not a known
* member.
*/
fun known(): Known =
when (this) {
COMMENTARY -> Known.COMMENTARY
FINAL_ANSWER -> Known.FINAL_ANSWER
else -> throw OpenAIInvalidDataException("Unknown Phase: $value")
}

/**
* Returns this class instance's primitive wire representation.
*
* This differs from the [toString] method because that method is primarily for debugging
* and generally doesn't throw.
*
* @throws OpenAIInvalidDataException if this class instance's value does not have the
* expected primitive type.
*/
fun asString(): String =
_value().asString().orElseThrow { OpenAIInvalidDataException("Value is not a String") }

private var validated: Boolean = false

fun validate(): Phase = apply {
if (validated) {
return@apply
}

known()
validated = true
}

fun isValid(): Boolean =
try {
validate()
true
} catch (e: OpenAIInvalidDataException) {
false
}

/**
* Returns a score indicating how many valid values are contained in this object
* recursively.
*
* Used for best match union deserialization.
*/
@JvmSynthetic internal fun validity(): Int = if (value() == Value._UNKNOWN) 0 else 1

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
}

return other is Phase && value == other.value
}

override fun hashCode() = value.hashCode()

override fun toString() = value.toString()
}

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
Expand All @@ -1390,15 +1565,16 @@ private constructor(
role == other.role &&
status == other.status &&
type == other.type &&
phase == other.phase &&
additionalProperties == other.additionalProperties
}

private val hashCode: Int by lazy {
Objects.hash(id, content, role, status, type, additionalProperties)
Objects.hash(id, content, role, status, type, phase, additionalProperties)
}

override fun hashCode(): Int = hashCode

override fun toString() =
"Message{id=$id, content=$content, role=$role, status=$status, type=$type, additionalProperties=$additionalProperties}"
"Message{id=$id, content=$content, role=$role, status=$status, type=$type, phase=$phase, additionalProperties=$additionalProperties}"
}
Loading
Loading