Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "3.5.1"
".": "3.5.2"
}
4 changes: 2 additions & 2 deletions .stats.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
configured_endpoints: 118
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-16cb18bed32bae8c5840fb39a1bf664026cc40463ad0c487dcb0df1bd3d72db0.yml
openapi_spec_hash: 4cb51b22f98dee1a90bc7add82d1d132
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-94b1e3cb0bdc616ff0c2f267c33dadd95f133b1f64e647aab6c64afb292b2793.yml
openapi_spec_hash: 2395319ac9befd59b6536ae7f9564a05
config_hash: 930dac3aa861344867e4ac84f037b5df
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
# Changelog

## 3.5.2 (2025-09-12)

Full Changelog: [v3.5.1...v3.5.2](https://github.com/openai/openai-java/compare/v3.5.1...v3.5.2)

### Chores

* **api:** Minor docs and type updates for realtime ([63ad148](https://github.com/openai/openai-java/commit/63ad148f1f998de1c9d66021037d7d5e04615022))
* improve formatter performance ([be0acb7](https://github.com/openai/openai-java/commit/be0acb7a7779c4df1bf3e4678c40f4bd6d2b50d6))
* **internal:** codegen related update ([18a0e64](https://github.com/openai/openai-java/commit/18a0e645fd3ea28957e55633f2a271287b56c312))
* **internal:** codegen related update ([ab87009](https://github.com/openai/openai-java/commit/ab870095a749e00bcc2e864476553f782d098525))
* **internal:** remove redundant deserializer symbols ([8c63a5b](https://github.com/openai/openai-java/commit/8c63a5b4983039ace361ab08b89a0c227a88cf90))

## 3.5.1 (2025-09-10)

Full Changelog: [v3.5.0...v3.5.1](https://github.com/openai/openai-java/compare/v3.5.0...v3.5.1)
Expand Down
14 changes: 7 additions & 7 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/3.5.1)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/3.5.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/3.5.1)
[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/3.5.2)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/3.5.2/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/3.5.2)

<!-- 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/3.5.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/3.5.2).

<!-- 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:3.5.1")
implementation("com.openai:openai-java:3.5.2")
```

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

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:3.5.1")
implementation("com.openai:openai-java-spring-boot-starter:3.5.2")
```

#### Maven
Expand All @@ -1351,7 +1351,7 @@ implementation("com.openai:openai-java-spring-boot-starter:3.5.1")
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-spring-boot-starter</artifactId>
<version>3.5.1</version>
<version>3.5.2</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 = "3.5.1" // x-release-please-version
version = "3.5.2" // x-release-please-version
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,15 @@ 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.JsonValue
import com.openai.core.MultipartField
import com.openai.core.Params
import com.openai.core.allMaxBy
import com.openai.core.checkKnown
import com.openai.core.checkRequired
import com.openai.core.getOrThrow
Expand Down Expand Up @@ -1220,7 +1214,6 @@ private constructor(
* object can be provided to tweak VAD detection parameters manually. If unset, the audio is
* transcribed as a single block.
*/
@JsonDeserialize(using = ChunkingStrategy.Deserializer::class)
@JsonSerialize(using = ChunkingStrategy.Serializer::class)
class ChunkingStrategy
private constructor(
Expand Down Expand Up @@ -1285,25 +1278,6 @@ private constructor(
false
}

/**
* Returns 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<Int> {
override fun visitAuto(auto: JsonValue) =
auto.let { if (it == JsonValue.from("auto")) 1 else 0 }

override fun visitVadConfig(vadConfig: VadConfig) = 1

override fun unknown(json: JsonValue?) = 0
}
)

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
Expand Down Expand Up @@ -1361,36 +1335,6 @@ private constructor(
}
}

internal class Deserializer : BaseDeserializer<ChunkingStrategy>(ChunkingStrategy::class) {

override fun ObjectCodec.deserialize(node: JsonNode): ChunkingStrategy {
val json = JsonValue.fromJsonNode(node)

val bestMatches =
sequenceOf(
tryDeserialize(node, jacksonTypeRef<JsonValue>())
?.let { ChunkingStrategy(auto = it, _json = json) }
?.takeIf { it.isValid() },
tryDeserialize(node, jacksonTypeRef<VadConfig>())?.let {
ChunkingStrategy(vadConfig = it, _json = json)
},
)
.filterNotNull()
.allMaxBy { it.validity() }
.toList()
return when (bestMatches.size) {
// This can happen if what we're deserializing is completely incompatible with
// all the possible variants (e.g. deserializing from array).
0 -> ChunkingStrategy(_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<ChunkingStrategy>(ChunkingStrategy::class) {

override fun serialize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,15 @@ 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.JsonValue
import com.openai.core.MultipartField
import com.openai.core.Params
import com.openai.core.allMaxBy
import com.openai.core.checkRequired
import com.openai.core.getOrThrow
import com.openai.core.http.Headers
Expand Down Expand Up @@ -1729,7 +1723,6 @@ private constructor(
* For `dall-e-2`, you can only provide one image, and it should be a square `png` file less
* than 4MB.
*/
@JsonDeserialize(using = Image.Deserializer::class)
@JsonSerialize(using = Image.Serializer::class)
class Image
private constructor(
Expand Down Expand Up @@ -1784,25 +1777,6 @@ private constructor(
false
}

/**
* Returns 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<Int> {
override fun visitInputStream(inputStream: InputStream) = 1

override fun visitInputStreams(inputStreams: List<InputStream>) =
inputStreams.size

override fun unknown(json: JsonValue?) = 0
}
)

override fun equals(other: Any?): Boolean {
if (this === other) {
return true
Expand Down Expand Up @@ -1855,36 +1829,6 @@ private constructor(
}
}

internal class Deserializer : BaseDeserializer<Image>(Image::class) {

override fun ObjectCodec.deserialize(node: JsonNode): Image {
val json = JsonValue.fromJsonNode(node)

val bestMatches =
sequenceOf(
tryDeserialize(node, jacksonTypeRef<InputStream>())?.let {
Image(inputStream = it, _json = json)
},
tryDeserialize(node, jacksonTypeRef<List<InputStream>>())?.let {
Image(inputStreams = 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 -> Image(_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<Image>(Image::class) {

override fun serialize(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,21 @@ import com.openai.errors.OpenAIInvalidDataException
import java.util.Collections
import java.util.Objects

/** Returned when the server VAD timeout is triggered for the input audio buffer. */
/**
* Returned when the Server VAD timeout is triggered for the input audio buffer. This is configured
* with `idle_timeout_ms` in the `turn_detection` settings of the session, and it indicates that
* there hasn't been any speech detected for the configured duration.
*
* The `audio_start_ms` and `audio_end_ms` fields indicate the segment of audio after the last model
* response up to the triggering time, as an offset from the beginning of audio written to the input
* audio buffer. This means it demarcates the segment of audio that was silent and the difference
* between the start and end values will roughly match the configured timeout.
*
* The empty audio will be committed to the conversation as an `input_audio` item (there will be a
* `input_audio_buffer.committed` event) and a model response will be generated. There may be speech
* that didn't trigger VAD but is still detected by the model, so the model may respond with
* something relevant to the conversation or a prompt to continue speaking.
*/
class InputAudioBufferTimeoutTriggered
private constructor(
private val audioEndMs: JsonField<Long>,
Expand All @@ -40,15 +54,17 @@ private constructor(
) : this(audioEndMs, audioStartMs, eventId, itemId, type, mutableMapOf())

/**
* Millisecond offset where speech ended within the buffered audio.
* Millisecond offset of audio written to the input audio buffer at the time the timeout was
* triggered.
*
* @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 audioEndMs(): Long = audioEndMs.getRequired("audio_end_ms")

/**
* Millisecond offset where speech started within the buffered audio.
* Millisecond offset of audio written to the input audio buffer that was after the playback
* time of the last model response.
*
* @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).
Expand Down Expand Up @@ -165,7 +181,10 @@ private constructor(
inputAudioBufferTimeoutTriggered.additionalProperties.toMutableMap()
}

/** Millisecond offset where speech ended within the buffered audio. */
/**
* Millisecond offset of audio written to the input audio buffer at the time the timeout was
* triggered.
*/
fun audioEndMs(audioEndMs: Long) = audioEndMs(JsonField.of(audioEndMs))

/**
Expand All @@ -176,7 +195,10 @@ private constructor(
*/
fun audioEndMs(audioEndMs: JsonField<Long>) = apply { this.audioEndMs = audioEndMs }

/** Millisecond offset where speech started within the buffered audio. */
/**
* Millisecond offset of audio written to the input audio buffer that was after the playback
* time of the last model response.
*/
fun audioStartMs(audioStartMs: Long) = audioStartMs(JsonField.of(audioStartMs))

/**
Expand Down
Loading