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.3"
".": "3.6.0"
}
6 changes: 3 additions & 3 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-d30ff992a48873c1466c49f3c01f2ec8933faebff23424748f8d056065b1bcef.yml
openapi_spec_hash: e933ec43b46f45c348adb78840e5808d
config_hash: bf45940f0a7805b4ec2017eecdd36893
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-ea23db36b0899cc715f56d0098956069b2d92880f448adff3a4ac1bb53cb2cec.yml
openapi_spec_hash: 36f76ea31297c9593bcfae453f6255cc
config_hash: 666d6bb4b564f0d9d431124b5d1a0665
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## 3.6.0 (2025-09-19)

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

### Features

* **api:** add reasoning_text ([2bde290](https://github.com/openai/openai-java/commit/2bde29036040664aa4ae54f6522e5e5336ed2c87))
* **api:** type updates for conversations, reasoning_effort and results for evals ([364a176](https://github.com/openai/openai-java/commit/364a1766d34b6cc19fca81a445b287fc9ccefe2c))
* **client:** expose sleeper option ([a5ffd57](https://github.com/openai/openai-java/commit/a5ffd57e75b6f0abe14d326c87f58abd2048cf73))


### Bug Fixes

* **client:** ensure single timer is created per client ([a5ffd57](https://github.com/openai/openai-java/commit/a5ffd57e75b6f0abe14d326c87f58abd2048cf73))

## 3.5.3 (2025-09-15)

Full Changelog: [v3.5.2...v3.5.3](https://github.com/openai/openai-java/compare/v3.5.2...v3.5.3)
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.3)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/3.5.3/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/3.5.3)
[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/3.6.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/3.6.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/3.6.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/3.5.3).
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.6.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:3.5.3")
implementation("com.openai:openai-java:3.6.0")
```

### Maven
Expand All @@ -33,7 +33,7 @@ implementation("com.openai:openai-java:3.5.3")
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java</artifactId>
<version>3.5.3</version>
<version>3.6.0</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.3")
implementation("com.openai:openai-java-spring-boot-starter:3.6.0")
```

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

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.openai.azure.AzureUrlPathMode
import com.openai.client.OpenAIClient
import com.openai.client.OpenAIClientImpl
import com.openai.core.ClientOptions
import com.openai.core.Sleeper
import com.openai.core.Timeout
import com.openai.core.http.AsyncStreamResponse
import com.openai.core.http.Headers
Expand Down Expand Up @@ -135,6 +136,17 @@ class OpenAIOkHttpClient private constructor() {
clientOptions.streamHandlerExecutor(streamHandlerExecutor)
}

/**
* The interface to use for delaying execution, like during retries.
*
* This is primarily useful for using fake delays in tests.
*
* Defaults to real execution delays.
*
* This class takes ownership of the sleeper and closes it when closed.
*/
fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }

/**
* The clock to use for operations that require timing, like retries.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import com.openai.azure.AzureUrlPathMode
import com.openai.client.OpenAIClientAsync
import com.openai.client.OpenAIClientAsyncImpl
import com.openai.core.ClientOptions
import com.openai.core.Sleeper
import com.openai.core.Timeout
import com.openai.core.http.AsyncStreamResponse
import com.openai.core.http.Headers
Expand Down Expand Up @@ -135,6 +136,17 @@ class OpenAIOkHttpClientAsync private constructor() {
clientOptions.streamHandlerExecutor(streamHandlerExecutor)
}

/**
* The interface to use for delaying execution, like during retries.
*
* This is primarily useful for using fake delays in tests.
*
* Defaults to real execution delays.
*
* This class takes ownership of the sleeper and closes it when closed.
*/
fun sleeper(sleeper: Sleeper) = apply { clientOptions.sleeper(sleeper) }

/**
* The clock to use for operations that require timing, like retries.
*
Expand Down
61 changes: 46 additions & 15 deletions openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ private constructor(
* This class takes ownership of the executor and shuts it down, if possible, when closed.
*/
@get:JvmName("streamHandlerExecutor") val streamHandlerExecutor: Executor,
/**
* The interface to use for delaying execution, like during retries.
*
* This is primarily useful for using fake delays in tests.
*
* Defaults to real execution delays.
*
* This class takes ownership of the sleeper and closes it when closed.
*/
@get:JvmName("sleeper") val sleeper: Sleeper,
/**
* The clock to use for operations that require timing, like retries.
*
Expand Down Expand Up @@ -162,6 +172,7 @@ private constructor(
private var checkJacksonVersionCompatibility: Boolean = true
private var jsonMapper: JsonMapper = jsonMapper()
private var streamHandlerExecutor: Executor? = null
private var sleeper: Sleeper? = null
private var clock: Clock = Clock.systemUTC()
private var baseUrl: String? = null
private var headers: Headers.Builder = Headers.builder()
Expand All @@ -182,6 +193,7 @@ private constructor(
checkJacksonVersionCompatibility = clientOptions.checkJacksonVersionCompatibility
jsonMapper = clientOptions.jsonMapper
streamHandlerExecutor = clientOptions.streamHandlerExecutor
sleeper = clientOptions.sleeper
clock = clientOptions.clock
baseUrl = clientOptions.baseUrl
headers = clientOptions.headers.toBuilder()
Expand Down Expand Up @@ -241,6 +253,17 @@ private constructor(
else streamHandlerExecutor
}

/**
* The interface to use for delaying execution, like during retries.
*
* This is primarily useful for using fake delays in tests.
*
* Defaults to real execution delays.
*
* This class takes ownership of the sleeper and closes it when closed.
*/
fun sleeper(sleeper: Sleeper) = apply { this.sleeper = PhantomReachableSleeper(sleeper) }

/**
* The clock to use for operations that require timing, like retries.
*
Expand Down Expand Up @@ -479,6 +502,25 @@ private constructor(
*/
fun build(): ClientOptions {
val httpClient = checkRequired("httpClient", httpClient)
val streamHandlerExecutor =
streamHandlerExecutor
?: PhantomReachableExecutorService(
Executors.newCachedThreadPool(
object : ThreadFactory {

private val threadFactory: ThreadFactory =
Executors.defaultThreadFactory()
private val count = AtomicLong(0)

override fun newThread(runnable: Runnable): Thread =
threadFactory.newThread(runnable).also {
it.name =
"openai-stream-handler-thread-${count.getAndIncrement()}"
}
}
)
)
val sleeper = sleeper ?: PhantomReachableSleeper(DefaultSleeper())
val credential = checkRequired("credential", credential)

val headers = Headers.builder()
Expand Down Expand Up @@ -530,26 +572,14 @@ private constructor(
httpClient,
RetryingHttpClient.builder()
.httpClient(httpClient)
.sleeper(sleeper)
.clock(clock)
.maxRetries(maxRetries)
.build(),
checkJacksonVersionCompatibility,
jsonMapper,
streamHandlerExecutor
?: Executors.newCachedThreadPool(
object : ThreadFactory {

private val threadFactory: ThreadFactory =
Executors.defaultThreadFactory()
private val count = AtomicLong(0)

override fun newThread(runnable: Runnable): Thread =
threadFactory.newThread(runnable).also {
it.name =
"openai-stream-handler-thread-${count.getAndIncrement()}"
}
}
),
streamHandlerExecutor,
sleeper,
clock,
baseUrl,
headers.build(),
Expand Down Expand Up @@ -580,5 +610,6 @@ private constructor(
fun close() {
httpClient.close()
(streamHandlerExecutor as? ExecutorService)?.shutdown()
sleeper.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.openai.core

import java.time.Duration
import java.util.Timer
import java.util.TimerTask
import java.util.concurrent.CompletableFuture

class DefaultSleeper : Sleeper {

private val timer = Timer("DefaultSleeper", true)

override fun sleep(duration: Duration) = Thread.sleep(duration.toMillis())

override fun sleepAsync(duration: Duration): CompletableFuture<Void> {
val future = CompletableFuture<Void>()
timer.schedule(
object : TimerTask() {
override fun run() {
future.complete(null)
}
},
duration.toMillis(),
)
return future
}

override fun close() = timer.cancel()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.openai.core

import java.time.Duration
import java.util.concurrent.CompletableFuture

/**
* A delegating wrapper around a [Sleeper] that closes it once it's only phantom reachable.
*
* This class ensures the [Sleeper] is closed even if the user forgets to do it.
*/
internal class PhantomReachableSleeper(private val sleeper: Sleeper) : Sleeper {

init {
closeWhenPhantomReachable(this, sleeper)
}

override fun sleep(duration: Duration) = sleeper.sleep(duration)

override fun sleepAsync(duration: Duration): CompletableFuture<Void> =
sleeper.sleepAsync(duration)

override fun close() = sleeper.close()
}
21 changes: 21 additions & 0 deletions openai-java-core/src/main/kotlin/com/openai/core/Sleeper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.openai.core

import java.time.Duration
import java.util.concurrent.CompletableFuture

/**
* An interface for delaying execution for a specified amount of time.
*
* Useful for testing and cleaning up resources.
*/
interface Sleeper : AutoCloseable {

/** Synchronously pauses execution for the given [duration]. */
fun sleep(duration: Duration)

/** Asynchronously pauses execution for the given [duration]. */
fun sleepAsync(duration: Duration): CompletableFuture<Void>

/** Overridden from [AutoCloseable] to not have a checked exception in its signature. */
override fun close()
}
Loading