Skip to content

release: 2.18.0 #547

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 4 commits into from
Jul 22, 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
31 changes: 31 additions & 0 deletions .github/workflows/publish-sonatype.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,37 @@ jobs:
- name: Set up Gradle
uses: gradle/gradle-build-action@v2

- name: Compile the openai-java-core project
run: |
./gradlew :openai-java-core:compileJava :openai-java-core:compileTestJava -x test

- name: Run the Prism server
run: |
./scripts/mock --daemon

- name: Setup GraalVM
uses: graalvm/setup-graalvm@v1
with:
java-version: 21
distribution: 'graalvm-community'
cache: gradle

- name: Run tests on the openai-java-core project with the GraalVM native-image agent
run: |
./gradlew :openai-java-core:test -x compileJava -x compileTestJava -x compileKotlin -x compileTestKotlin -PgraalvmAgent

- name: Check generated GraalVM file
run: |
echo "Checking for GraalVM agent metadata files..."
DIRECTORY=openai-java-core/src/main/resources/META-INF/native-image
if [ -d "$DIRECTORY" ] && [ "$(ls -A $DIRECTORY)" ]; then
echo "Files found in $DIRECTORY:"
ls -l $DIRECTORY
else
echo "No files found in $DIRECTORY"
exit 1
fi

- name: Publish to Sonatype
run: |-
export -- GPG_SIGNING_KEY_ID
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 @@
{
".": "2.17.0"
".": "2.18.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: 88
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-670ea0d2cc44f52a87dd3cadea45632953283e0636ba30788fdbdb22a232ccac.yml
openapi_spec_hash: d8b7d38911fead545adf3e4297956410
config_hash: b2a4028fdbb27a08de89831ed310e244
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai%2Fopenai-b2a451656ca64d30d174391ebfd94806b4de3ab76dc55b92843cfb7f1a54ecb6.yml
openapi_spec_hash: 27d9691b400f28c17ef063a1374048b0
config_hash: e822d0c9082c8b312264403949243179
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 2.18.0 (2025-07-22)

Full Changelog: [v2.17.0...v2.18.0](https://github.com/openai/openai-java/compare/v2.17.0...v2.18.0)

### Features

* **client:** add `{QueryParams,Headers}#put(String, JsonValue)` methods ([1973f9a](https://github.com/openai/openai-java/commit/1973f9a7049505a27a7ec908549254f192d74be0))
* **client:** allow configuring env via system properties ([5f8deb3](https://github.com/openai/openai-java/commit/5f8deb34ebe2982dee1aa6fd1bb1a08122a80a8d))


### Chores

* **api:** event shapes more accurate ([b7ba592](https://github.com/openai/openai-java/commit/b7ba592a4b0ebdcf53719017afb064227136d59d))

## 2.17.0 (2025-07-21)

Full Changelog: [v2.16.0...v2.17.0](https://github.com/openai/openai-java/compare/v2.16.0...v2.17.0)
Expand Down
124 changes: 106 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,29 @@

<!-- 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/2.17.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/2.17.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/2.17.0)
[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/2.18.0)
[![javadoc](https://javadoc.io/badge2/com.openai/openai-java/2.18.0/javadoc.svg)](https://javadoc.io/doc/com.openai/openai-java/2.18.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/2.17.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/2.18.0).

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

## Installation

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

[_Try `openai-java-spring-boot-starter` if you're using Spring Boot!_](#spring-boot)

### Gradle

```kotlin
implementation("com.openai:openai-java:2.17.0")
implementation("com.openai:openai-java:2.18.0")
```

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

Expand Down Expand Up @@ -73,7 +75,8 @@ import com.openai.models.ChatModel;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;

// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
// Configures using the `openai.apiKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();

ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
Expand All @@ -85,13 +88,14 @@ ChatCompletion chatCompletion = client.chat().completions().create(params);

## Client configuration

Configure the client using environment variables:
Configure the client using system properties or environment variables:

```java
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;

// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
// Configures using the `openai.apiKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();
```

Expand All @@ -113,21 +117,24 @@ import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;

OpenAIClient client = OpenAIOkHttpClient.builder()
// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
// Configures using the `openai.apiKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
Or configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
.fromEnv()
.apiKey("My API Key")
.build();
```

See this table for the available options:

| Setter | Environment variable | Required | Default value |
| --------------- | ----------------------- | -------- | ----------------------------- |
| `apiKey` | `OPENAI_API_KEY` | true | - |
| `organization` | `OPENAI_ORG_ID` | false | - |
| `project` | `OPENAI_PROJECT_ID` | false | - |
| `webhookSecret` | `OPENAI_WEBHOOK_SECRET` | false | - |
| `baseUrl` | `OPENAI_BASE_URL` | true | `"https://api.openai.com/v1"` |
| Setter | System property | Environment variable | Required | Default value |
| --------------- | ---------------------- | ----------------------- | -------- | ----------------------------- |
| `apiKey` | `openai.apiKey` | `OPENAI_API_KEY` | true | - |
| `organization` | `openai.orgId` | `OPENAI_ORG_ID` | false | - |
| `project` | `openai.projectId` | `OPENAI_PROJECT_ID` | false | - |
| `webhookSecret` | `openai.webhookSecret` | `OPENAI_WEBHOOK_SECRET` | false | - |
| `baseUrl` | `openai.baseUrl` | `OPENAI_BASE_URL` | true | `"https://api.openai.com/v1"` |

System properties take precedence over environment variables.

> [!TIP]
> Don't create more than one client in the same application. Each client has a connection pool and
Expand Down Expand Up @@ -174,7 +181,8 @@ import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.concurrent.CompletableFuture;

// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
// Configures using the `openai.apiKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClient client = OpenAIOkHttpClient.fromEnv();

ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
Expand All @@ -194,7 +202,8 @@ import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
import java.util.concurrent.CompletableFuture;

// Configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
// Configures using the `openai.apiKey`, `openai.orgId`, `openai.projectId`, `openai.webhookSecret` and `openai.baseUrl` system properties
// Or configures using the `OPENAI_API_KEY`, `OPENAI_ORG_ID`, `OPENAI_PROJECT_ID`, `OPENAI_WEBHOOK_SECRET` and `OPENAI_BASE_URL` environment variables
OpenAIClientAsync client = OpenAIOkHttpClientAsync.fromEnv();

ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
Expand Down Expand Up @@ -1295,6 +1304,85 @@ Or to `debug` for more verbose logging:
$ export OPENAI_LOG=debug
```

## GraalVM

Although the SDK uses reflection, it is still usable in [GraalVM](https://www.graalvm.org) because `openai-java-core` is published with [reachability metadata](https://www.graalvm.org/latest/reference-manual/native-image/metadata/).

GraalVM should automatically detect and use the published metadata, but [manual configuration](https://www.graalvm.org/jdk24/reference-manual/native-image/overview/BuildConfiguration/) is also available.

## Spring Boot

If you're using Spring Boot, then you can use the SDK's [Spring Boot starter](https://docs.spring.io/spring-boot/docs/2.7.18/reference/htmlsingle/#using.build-systems.starters) to simplify configuration and get set up quickly.

### Installation

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

#### Gradle

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

#### Maven

```xml
<dependency>
<groupId>com.openai</groupId>
<artifactId>openai-java-spring-boot-starter</artifactId>
<version>2.18.0</version>
</dependency>
```

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

### Configuration

The [client's environment variable options](#client-configuration) can be configured in [`application.properties` or `application.yml`](https://docs.spring.io/spring-boot/how-to/properties-and-configuration.html).

#### `application.properties`

```properties
openai.base-url=https://api.openai.com/v1
openai.api-key=My API Key
openai.org-id=My Organization
openai.project-id=My Project
openai.webhook-secret=My Webhook Secret
```

#### `application.yml`

```yaml
openai:
base-url: https://api.openai.com/v1
api-key: My API Key
org-id: My Organization
project-id: My Project
webhook-secret: My Webhook Secret
```

#### Other configuration

Configure any other client option by providing one or more instances of [`OpenAIClientCustomizer`](openai-java-core/src/main/kotlin/com/openai/springboot/OpenAIClientCustomizer.kt). For example, here's how you'd set [`maxRetries`](#retries):

```java
import com.openai.springboot.OpenAIClientCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class OpenAIConfig {
@Bean
public OpenAIClientCustomizer customizer() {
return builder -> builder.maxRetries(3);
}
}
```

### Usage

[Inject](https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html) [`OpenAIClient`](openai-java-core/src/main/kotlin/com/openai/client/OpenAIClient.kt) anywhere and start using it!

## 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.
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 = "2.17.0" // x-release-please-version
version = "2.18.0" // x-release-please-version
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
// File generated from our OpenAPI spec by Stainless.

package com.openai.core.http

import com.openai.core.JsonArray
import com.openai.core.JsonBoolean
import com.openai.core.JsonMissing
import com.openai.core.JsonNull
import com.openai.core.JsonNumber
import com.openai.core.JsonObject
import com.openai.core.JsonString
import com.openai.core.JsonValue
import com.openai.core.toImmutable
import java.util.TreeMap

Expand Down Expand Up @@ -28,6 +38,19 @@ private constructor(
TreeMap(String.CASE_INSENSITIVE_ORDER)
private var size: Int = 0

fun put(name: String, value: JsonValue): Builder = apply {
when (value) {
is JsonMissing,
is JsonNull -> {}
is JsonBoolean -> put(name, value.value.toString())
is JsonNumber -> put(name, value.value.toString())
is JsonString -> put(name, value.value)
is JsonArray -> value.values.forEach { put(name, it) }
is JsonObject ->
value.values.forEach { (nestedName, value) -> put("$name.$nestedName", value) }
}
}

fun put(name: String, value: String) = apply {
map.getOrPut(name) { mutableListOf() }.add(value)
size++
Expand All @@ -41,15 +64,6 @@ private constructor(
headers.names().forEach { put(it, headers.values(it)) }
}

fun remove(name: String) = apply { size -= map.remove(name).orEmpty().size }

fun removeAll(names: Set<String>) = apply { names.forEach(::remove) }

fun clear() = apply {
map.clear()
size = 0
}

fun replace(name: String, value: String) = apply {
remove(name)
put(name, value)
Expand All @@ -68,6 +82,15 @@ private constructor(
headers.names().forEach { replace(it, headers.values(it)) }
}

fun remove(name: String) = apply { size -= map.remove(name).orEmpty().size }

fun removeAll(names: Set<String>) = apply { names.forEach(::remove) }

fun clear() = apply {
map.clear()
size = 0
}

fun build() =
Headers(
map.mapValuesTo(TreeMap(String.CASE_INSENSITIVE_ORDER)) { (_, values) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

package com.openai.core.http

import com.openai.core.JsonArray
import com.openai.core.JsonBoolean
import com.openai.core.JsonMissing
import com.openai.core.JsonNull
import com.openai.core.JsonNumber
import com.openai.core.JsonObject
import com.openai.core.JsonString
import com.openai.core.JsonValue
import com.openai.core.toImmutable

class QueryParams
Expand All @@ -28,6 +36,19 @@ private constructor(
private val map: MutableMap<String, MutableList<String>> = mutableMapOf()
private var size: Int = 0

fun put(key: String, value: JsonValue): Builder = apply {
when (value) {
is JsonMissing,
is JsonNull -> {}
is JsonBoolean -> put(key, value.value.toString())
is JsonNumber -> put(key, value.value.toString())
is JsonString -> put(key, value.value)
is JsonArray -> value.values.forEach { put("$key[]", it) }
is JsonObject ->
value.values.forEach { (nestedKey, value) -> put("$key[$nestedKey]", value) }
}
}

fun put(key: String, value: String) = apply {
map.getOrPut(key) { mutableListOf() }.add(value)
size++
Expand Down
Loading