Skip to content

release: 1.4.0 #443

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Apr 17, 2025
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 .github/workflows/publish-sonatype.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
export -- GPG_SIGNING_KEY_ID
printenv -- GPG_SIGNING_KEY | gpg --batch --passphrase-fd 3 --import 3<<< "$GPG_SIGNING_PASSWORD"
GPG_SIGNING_KEY_ID="$(gpg --with-colons --list-keys | awk -F : -- '/^pub:/ { getline; print "0x" substr($10, length($10) - 7) }')"
./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD"
./gradlew publishAndReleaseToMavenCentral --stacktrace -PmavenCentralUsername="$SONATYPE_USERNAME" -PmavenCentralPassword="$SONATYPE_PASSWORD" --no-configuration-cache
env:
SONATYPE_USERNAME: ${{ secrets.OPENAI_SONATYPE_USERNAME || secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.OPENAI_SONATYPE_PASSWORD || secrets.SONATYPE_PASSWORD }}
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.3.1"
".": "1.4.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: 95
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-a555f81249cb084f463dcefa4aba069f9341fdaf3dd6ac27d7f237fc90e8f488.yml
openapi_spec_hash: 8e590296cd1a54b9508510b0c7a2c45a
config_hash: 5ea32de61ff42fcf5e66cff8d9e247ea
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-5633633cc38734869cf7d993f7b549bb8e4d10e0ec45381ec2cd91507cd8eb8f.yml
openapi_spec_hash: c855121b2b2324b99499c9244c21d24d
config_hash: d20837393b73efdb19cd08e04c1cc9a1
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Changelog

## 1.4.0 (2025-04-17)

Full Changelog: [v1.3.1...v1.4.0](https://github.com/openai/openai-java/compare/v1.3.1...v1.4.0)

### Features

* **api:** add o3 and o4-mini model IDs ([069db4f](https://github.com/openai/openai-java/commit/069db4f94021cfc2f8d3a5a969453efae5f126a1))


### Performance Improvements

* **internal:** improve compilation+test speed ([ef38b5b](https://github.com/openai/openai-java/commit/ef38b5bfd278774e056d21a795845fb5c3a99f0a))


### Documentation

* explain http client customization ([da22f7d](https://github.com/openai/openai-java/commit/da22f7d3f717882c555fbb6626ec842fc953def4))
* explain jackson compat in readme ([6bc1dbd](https://github.com/openai/openai-java/commit/6bc1dbd2d95d2fb235bb9506a766b9f083017bc2))

## 1.3.1 (2025-04-16)

Full Changelog: [v1.3.0...v1.3.1](https://github.com/openai/openai-java/compare/v1.3.0...v1.3.1)
Expand Down
59 changes: 53 additions & 6 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/1.3.1)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.3.1/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.3.1)
[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/1.4.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/1.4.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/1.4.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/1.3.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/1.4.0).

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

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

```kotlin
implementation("com.openai:openai-java:1.3.1")
implementation("com.openai:openai-java:1.4.0")
```

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

Expand Down Expand Up @@ -59,7 +59,7 @@ OpenAIClient client = OpenAIOkHttpClient.fromEnv();

ResponseCreateParams params = ResponseCreateParams.builder()
.input("Say this is a test")
.model(ChatModel.GPT_4O)
.model(ChatModel.GPT_4_1)
.build();
Response response = client.responses().create(params);
```
Expand Down Expand Up @@ -594,6 +594,17 @@ Or to `debug` for more verbose logging:
$ export OPENAI_LOG=debug
```

## Jackson

The SDK depends on [Jackson](https://github.com/FasterXML/jackson) for JSON serialization/deserialization. It is compatible with version 2.13.4 or higher, but depends on version 2.18.2 by default.

The SDK throws an exception if it detects an incompatible Jackson version at runtime (e.g. if the default version was overridden in your Maven or Gradle config).

If the SDK threw an exception, but you're _certain_ the version is compatible, then disable the version check using the `checkJacksonVersionCompatibility` on [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt).

> [!CAUTION]
> We make no guarantee that the SDK works correctly when the Jackson version check is disabled.

## Microsoft Azure

To use this library with [Azure OpenAI](https://learn.microsoft.com/azure/ai-services/openai/overview), use the same
Expand Down Expand Up @@ -688,6 +699,42 @@ OpenAIClient client = OpenAIOkHttpClient.builder()
.build();
```

### Custom HTTP client

The SDK consists of three artifacts:

- `openai-java-core`
- Contains core SDK logic
- Does not depend on [OkHttp](https://square.github.io/okhttp)
- Exposes [`OpenAIClient`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt), [`OpenAIClientAsync`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsync.kt), [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt), and [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), all of which can work with any HTTP client
- `openai-java-client-okhttp`
- Depends on [OkHttp](https://square.github.io/okhttp)
- Exposes [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) and [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), which provide a way to construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) and [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), respectively, using OkHttp
- `openai-java`
- Depends on and exposes the APIs of both `openai-java-core` and `openai-java-client-okhttp`
- Does not have its own logic

This structure allows replacing the SDK's default HTTP client without pulling in unnecessary dependencies.

#### Customized [`OkHttpClient`](https://square.github.io/okhttp/3.x/okhttp/okhttp3/OkHttpClient.html)

> [!TIP]
> Try the available [network options](#network-options) before replacing the default client.

To use a customized `OkHttpClient`:

1. Replace your [`openai-java` dependency](#installation) with `openai-java-core`
2. Copy `openai-java-client-okhttp`'s [`OkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OkHttpClient.kt) class into your code and customize it
3. Construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) or [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), similarly to [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), using your customized client

### Completely custom HTTP client

To use a completely custom HTTP client:

1. Replace your [`openai-java` dependency](#installation) with `openai-java-core`
2. Write a class that implements the [`HttpClient`](openai-java-core/src/main/kotlin/com/openai/core/http/HttpClient.kt) interface
3. Construct [`OpenAIClientImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientImpl.kt) or [`OpenAIClientAsyncImpl`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClientAsyncImpl.kt), similarly to [`OpenAIOkHttpClient`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClient.kt) or [`OpenAIOkHttpClientAsync`](openai-java-client-okhttp/src/main/kotlin/com/openai/client/okhttp/OpenAIOkHttpClientAsync.kt), using your new client class

## Undocumented API functionality

The SDK is typed for convenient usage of the documented API. However, it also supports working with undocumented or not yet supported parts of the API.
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 = "1.3.1" // x-release-please-version
version = "1.4.0" // x-release-please-version
}

subprojects {
Expand Down
9 changes: 5 additions & 4 deletions buildSrc/src/main/kotlin/openai.kotlin.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {

kotlin {
jvmToolchain {
languageVersion.set(JavaLanguageVersion.of(17))
languageVersion.set(JavaLanguageVersion.of(21))
}

compilerOptions {
Expand All @@ -19,6 +19,8 @@ kotlin {
// Suppress deprecation warnings because we may still reference and test deprecated members.
// TODO: Replace with `-Xsuppress-warning=DEPRECATION` once we use Kotlin compiler 2.1.0+.
"-nowarn",
// Use as many threads as there are CPU cores on the machine for compilation.
"-Xbackend-threads=0",
)
jvmTarget.set(JvmTarget.JVM_1_8)
languageVersion.set(KotlinVersion.KOTLIN_1_8)
Expand All @@ -34,8 +36,7 @@ configure<SpotlessExtension> {
}
}

// Run tests in parallel to some degree.
tasks.withType<Test>().configureEach {
maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
forkEvery = 100
systemProperty("junit.jupiter.execution.parallel.enabled", true)
systemProperty("junit.jupiter.execution.parallel.mode.default", "concurrent")
}
16 changes: 14 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,17 @@
org.gradle.caching=true
org.gradle.configuration-cache=true
org.gradle.parallel=true
org.gradle.daemon=false
org.gradle.jvmargs=-Xmx4g
kotlin.daemon.jvmargs=-Xmx4g
# These options improve our compilation and test performance. They are inherited by the Kotlin daemon.
org.gradle.jvmargs=\
-Xms1g \
-Xmx4g \
-XX:+UseParallelGC \
-XX:InitialCodeCacheSize=256m \
-XX:ReservedCodeCacheSize=1G \
-XX:MetaspaceSize=256m \
-XX:TieredStopAtLevel=1 \
-XX:GCTimeRatio=4 \
-XX:CICompilerCount=4 \
-XX:+OptimizeStringConcat \
-XX:+UseStringDeduplication
2 changes: 2 additions & 0 deletions openai-java-core/src/main/kotlin/com/openai/core/Check.kt
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ This can happen if you are either:
2. Depending on some library that depends on different Jackson versions, potentially transitively

Double-check that you are depending on compatible Jackson versions.

See https://www.github.com/openai/openai-java#jackson for more information.
"""
.trimIndent()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import kotlin.math.pow
class RetryingHttpClient
private constructor(
private val httpClient: HttpClient,
private val sleeper: Sleeper,
private val clock: Clock,
private val maxRetries: Int,
private val idempotencyHeader: String?,
Expand Down Expand Up @@ -62,10 +63,10 @@ private constructor(
null
}

val backoffMillis = getRetryBackoffMillis(retries, response)
val backoffDuration = getRetryBackoffDuration(retries, response)
// All responses must be closed, so close the failed one before retrying.
response?.close()
Thread.sleep(backoffMillis.toMillis())
sleeper.sleep(backoffDuration)
}
}

Expand Down Expand Up @@ -111,10 +112,10 @@ private constructor(
}
}

val backoffMillis = getRetryBackoffMillis(retries, response)
val backoffDuration = getRetryBackoffDuration(retries, response)
// All responses must be closed, so close the failed one before retrying.
response?.close()
return sleepAsync(backoffMillis.toMillis()).thenCompose {
return sleeper.sleepAsync(backoffDuration).thenCompose {
executeWithRetries(requestWithRetryCount, requestOptions)
}
}
Expand Down Expand Up @@ -179,7 +180,7 @@ private constructor(
// retried.
throwable is IOException || throwable is OpenAIIoException

private fun getRetryBackoffMillis(retries: Int, response: HttpResponse?): Duration {
private fun getRetryBackoffDuration(retries: Int, response: HttpResponse?): Duration {
// About the Retry-After header:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
response
Expand Down Expand Up @@ -226,33 +227,40 @@ private constructor(

companion object {

private val TIMER = Timer("RetryingHttpClient", true)

private fun sleepAsync(millis: Long): CompletableFuture<Void> {
val future = CompletableFuture<Void>()
TIMER.schedule(
object : TimerTask() {
override fun run() {
future.complete(null)
}
},
millis,
)
return future
}

@JvmStatic fun builder() = Builder()
}

class Builder internal constructor() {

private var httpClient: HttpClient? = null
private var sleeper: Sleeper =
object : Sleeper {

private val timer = Timer("RetryingHttpClient", 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
}
}
private var clock: Clock = Clock.systemUTC()
private var maxRetries: Int = 2
private var idempotencyHeader: String? = null

fun httpClient(httpClient: HttpClient) = apply { this.httpClient = httpClient }

@JvmSynthetic internal fun sleeper(sleeper: Sleeper) = apply { this.sleeper = sleeper }

fun clock(clock: Clock) = apply { this.clock = clock }

fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }
Expand All @@ -262,9 +270,17 @@ private constructor(
fun build(): HttpClient =
RetryingHttpClient(
checkRequired("httpClient", httpClient),
sleeper,
clock,
maxRetries,
idempotencyHeader,
)
}

internal interface Sleeper {

fun sleep(duration: Duration)

fun sleepAsync(duration: Duration): CompletableFuture<Void>
}
}
24 changes: 24 additions & 0 deletions openai-java-core/src/main/kotlin/com/openai/models/ChatModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField<St

@JvmField val GPT_4_1_NANO_2025_04_14 = of("gpt-4.1-nano-2025-04-14")

@JvmField val O4_MINI = of("o4-mini")

@JvmField val O4_MINI_2025_04_16 = of("o4-mini-2025-04-16")

@JvmField val O3 = of("o3")

@JvmField val O3_2025_04_16 = of("o3-2025-04-16")

@JvmField val O3_MINI = of("o3-mini")

@JvmField val O3_MINI_2025_01_31 = of("o3-mini-2025-01-31")
Expand Down Expand Up @@ -131,6 +139,10 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField<St
GPT_4_1_2025_04_14,
GPT_4_1_MINI_2025_04_14,
GPT_4_1_NANO_2025_04_14,
O4_MINI,
O4_MINI_2025_04_16,
O3,
O3_2025_04_16,
O3_MINI,
O3_MINI_2025_01_31,
O1,
Expand Down Expand Up @@ -192,6 +204,10 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField<St
GPT_4_1_2025_04_14,
GPT_4_1_MINI_2025_04_14,
GPT_4_1_NANO_2025_04_14,
O4_MINI,
O4_MINI_2025_04_16,
O3,
O3_2025_04_16,
O3_MINI,
O3_MINI_2025_01_31,
O1,
Expand Down Expand Up @@ -254,6 +270,10 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField<St
GPT_4_1_2025_04_14 -> Value.GPT_4_1_2025_04_14
GPT_4_1_MINI_2025_04_14 -> Value.GPT_4_1_MINI_2025_04_14
GPT_4_1_NANO_2025_04_14 -> Value.GPT_4_1_NANO_2025_04_14
O4_MINI -> Value.O4_MINI
O4_MINI_2025_04_16 -> Value.O4_MINI_2025_04_16
O3 -> Value.O3
O3_2025_04_16 -> Value.O3_2025_04_16
O3_MINI -> Value.O3_MINI
O3_MINI_2025_01_31 -> Value.O3_MINI_2025_01_31
O1 -> Value.O1
Expand Down Expand Up @@ -316,6 +336,10 @@ class ChatModel @JsonCreator private constructor(private val value: JsonField<St
GPT_4_1_2025_04_14 -> Known.GPT_4_1_2025_04_14
GPT_4_1_MINI_2025_04_14 -> Known.GPT_4_1_MINI_2025_04_14
GPT_4_1_NANO_2025_04_14 -> Known.GPT_4_1_NANO_2025_04_14
O4_MINI -> Known.O4_MINI
O4_MINI_2025_04_16 -> Known.O4_MINI_2025_04_16
O3 -> Known.O3
O3_2025_04_16 -> Known.O3_2025_04_16
O3_MINI -> Known.O3_MINI
O3_MINI_2025_01_31 -> Known.O3_MINI_2025_01_31
O1 -> Known.O1
Expand Down
Loading