From 18d80a26f050a9303870e16a8cb72ea83f5e78ae Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Sat, 15 Aug 2026 11:24:49 +0200 Subject: [PATCH 1/2] feat(bedrock)!: move BedrockOpenAIOkHttpClient to com.openai.bedrock `openai-java-bedrock` shipped `BedrockOpenAIOkHttpClient` in the `com.openai.client.okhttp` package, which is owned by `openai-java-client-okhttp`. Because the Bedrock artifact declares a compile-scope dependency on the OkHttp artifact, every consumer has both JARs on the path, so the two always split that package. On the class path this is harmless, but it makes the Bedrock artifact unusable on the module path: java.lang.module.ResolutionException: Modules com.openai.client.okhttp and com.openai.bedrock export package com.openai.client.okhttp to module com.openai.core Relocating the class is the only fix: a deprecated forwarding class left behind in the old package would keep the package present in both JARs and preserve the split. The class only ever used public `OpenAIOkHttpClient` builder methods, so its original placement was cosmetic rather than load-bearing. BREAKING CHANGE: `BedrockOpenAIOkHttpClient` moved from `com.openai.client.okhttp` to `com.openai.bedrock`. Update imports to `import com.openai.bedrock.BedrockOpenAIOkHttpClient;`. Claude-Session: https://claude.ai/code/session_01193YsGKVxz9nkiW6qpKvFP --- README.md | 2 +- bedrock.md | 2 +- .../{client/okhttp => bedrock}/BedrockOpenAIOkHttpClient.kt | 5 ++--- .../okhttp => bedrock}/BedrockOpenAIOkHttpClientTest.kt | 2 +- .../java/com/openai/example/BedrockResponsesExample.java | 2 +- .../example/BedrockResponsesStreamingAsyncExample.java | 2 +- .../java/com/openai/compatibility/BedrockRuntimeProbe.java | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) rename openai-java-bedrock/src/main/kotlin/com/openai/{client/okhttp => bedrock}/BedrockOpenAIOkHttpClient.kt (98%) rename openai-java-bedrock/src/test/kotlin/com/openai/{client/okhttp => bedrock}/BedrockOpenAIOkHttpClientTest.kt (99%) diff --git a/README.md b/README.md index 4b181cf58..b22279369 100644 --- a/README.md +++ b/README.md @@ -93,8 +93,8 @@ implementation("com.openai:openai-java-bedrock:4.51.0") ```java +import com.openai.bedrock.BedrockOpenAIOkHttpClient; import com.openai.client.OpenAIClient; -import com.openai.client.okhttp.BedrockOpenAIOkHttpClient; // Uses the standard AWS credential chain, including environment credentials, // ~/.aws/credentials, AWS_PROFILE, workload roles, and instance metadata. diff --git a/bedrock.md b/bedrock.md index db928aa82..50130dd39 100644 --- a/bedrock.md +++ b/bedrock.md @@ -29,8 +29,8 @@ implementation("com.openai:openai-java-bedrock:4.51.0") Configure AWS credentials as you normally would, then provide the region: ```java +import com.openai.bedrock.BedrockOpenAIOkHttpClient; import com.openai.client.OpenAIClient; -import com.openai.client.okhttp.BedrockOpenAIOkHttpClient; OpenAIClient client = BedrockOpenAIOkHttpClient.builder() .awsRegion("us-east-1") diff --git a/openai-java-bedrock/src/main/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClient.kt b/openai-java-bedrock/src/main/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClient.kt similarity index 98% rename from openai-java-bedrock/src/main/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClient.kt rename to openai-java-bedrock/src/main/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClient.kt index eead9cc9b..68b81546e 100644 --- a/openai-java-bedrock/src/main/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClient.kt +++ b/openai-java-bedrock/src/main/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClient.kt @@ -1,9 +1,8 @@ -package com.openai.client.okhttp +package com.openai.bedrock import com.fasterxml.jackson.databind.json.JsonMapper -import com.openai.bedrock.BedrockAuthOptions -import com.openai.bedrock.resolve import com.openai.client.OpenAIClient +import com.openai.client.okhttp.OpenAIOkHttpClient import com.openai.core.LogLevel import com.openai.core.Sleeper import com.openai.core.Timeout diff --git a/openai-java-bedrock/src/test/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClientTest.kt b/openai-java-bedrock/src/test/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClientTest.kt similarity index 99% rename from openai-java-bedrock/src/test/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClientTest.kt rename to openai-java-bedrock/src/test/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClientTest.kt index 145c24bd6..33f398a54 100644 --- a/openai-java-bedrock/src/test/kotlin/com/openai/client/okhttp/BedrockOpenAIOkHttpClientTest.kt +++ b/openai-java-bedrock/src/test/kotlin/com/openai/bedrock/BedrockOpenAIOkHttpClientTest.kt @@ -1,4 +1,4 @@ -package com.openai.client.okhttp +package com.openai.bedrock import com.github.tomakehurst.wiremock.client.WireMock.findAll import com.github.tomakehurst.wiremock.client.WireMock.get diff --git a/openai-java-example/src/main/java/com/openai/example/BedrockResponsesExample.java b/openai-java-example/src/main/java/com/openai/example/BedrockResponsesExample.java index 318521cf2..39595d723 100644 --- a/openai-java-example/src/main/java/com/openai/example/BedrockResponsesExample.java +++ b/openai-java-example/src/main/java/com/openai/example/BedrockResponsesExample.java @@ -1,7 +1,7 @@ package com.openai.example; +import com.openai.bedrock.BedrockOpenAIOkHttpClient; import com.openai.client.OpenAIClient; -import com.openai.client.okhttp.BedrockOpenAIOkHttpClient; import com.openai.models.responses.ResponseCreateParams; public final class BedrockResponsesExample { diff --git a/openai-java-example/src/main/java/com/openai/example/BedrockResponsesStreamingAsyncExample.java b/openai-java-example/src/main/java/com/openai/example/BedrockResponsesStreamingAsyncExample.java index cb2492571..651b8ab8a 100644 --- a/openai-java-example/src/main/java/com/openai/example/BedrockResponsesStreamingAsyncExample.java +++ b/openai-java-example/src/main/java/com/openai/example/BedrockResponsesStreamingAsyncExample.java @@ -1,7 +1,7 @@ package com.openai.example; +import com.openai.bedrock.BedrockOpenAIOkHttpClient; import com.openai.client.OpenAIClientAsync; -import com.openai.client.okhttp.BedrockOpenAIOkHttpClient; import com.openai.models.responses.ResponseCreateParams; public final class BedrockResponsesStreamingAsyncExample { diff --git a/openai-java-runtime-compatibility/src/main/java/com/openai/compatibility/BedrockRuntimeProbe.java b/openai-java-runtime-compatibility/src/main/java/com/openai/compatibility/BedrockRuntimeProbe.java index 18428f799..08346d28d 100644 --- a/openai-java-runtime-compatibility/src/main/java/com/openai/compatibility/BedrockRuntimeProbe.java +++ b/openai-java-runtime-compatibility/src/main/java/com/openai/compatibility/BedrockRuntimeProbe.java @@ -1,7 +1,7 @@ package com.openai.compatibility; +import com.openai.bedrock.BedrockOpenAIOkHttpClient; import com.openai.client.OpenAIClient; -import com.openai.client.okhttp.BedrockOpenAIOkHttpClient; public final class BedrockRuntimeProbe { private static final String[] RUNTIME_PROVIDER_CLASSES = { From 5dcaf261b4045362edf29b092b0d6f91d523daad Mon Sep 17 00:00:00 2001 From: Rafael Winterhalter Date: Sat, 15 Aug 2026 11:24:57 +0200 Subject: [PATCH 2/2] feat(build): declare automatic module names for published artifacts The published JARs carried no `Automatic-Module-Name`, so consumers on the module path got a name derived from the JAR file name (`openai.java.core`, `openai.java.client.okhttp`, ...). Those names are neither namespaced nor stable across a rename, and nothing stopped them from changing between releases. Derive the names from the `com.openai` group ID and the packages each artifact ships: openai-java com.openai openai-java-core com.openai.core openai-java-client-okhttp com.openai.client.okhttp openai-java-bedrock com.openai.bedrock The mapping lives in the publish convention plugin, so it applies to exactly the four published artifacts, and a `checkNotNull` guard fails configuration if a newly published artifact is added without a name. This mirrors how the root build guards the version support policy. These names are effectively public API: once released, changing one breaks every consumer that `requires` it. Claude-Session: https://claude.ai/code/session_01193YsGKVxz9nkiW6qpKvFP --- README.md | 15 +++++++++++ .../src/main/kotlin/openai.publish.gradle.kts | 25 +++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/README.md b/README.md index b22279369..2dbd59c3f 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,21 @@ implementation("com.openai:openai-java:4.51.0") The framework-neutral SDK artifacts require Java 8 or later. Runtime floors and lifecycle states are declared per artifact in the [Java version support policy](docs/version-support-policy.md). +### Java Platform Module System + +The published artifacts are not modular JARs, but each one declares a stable `Automatic-Module-Name` +so it can be placed on the module path: + +| Artifact | Module name | +| --- | --- | +| `openai-java` | `com.openai` | +| `openai-java-core` | `com.openai.core` | +| `openai-java-client-okhttp` | `com.openai.client.okhttp` | +| `openai-java-bedrock` | `com.openai.bedrock` | + +Each package is contained in exactly one artifact, so these modules can be resolved together on the +module path. + ## Usage > [!TIP] diff --git a/buildSrc/src/main/kotlin/openai.publish.gradle.kts b/buildSrc/src/main/kotlin/openai.publish.gradle.kts index c645c2ed9..499046443 100644 --- a/buildSrc/src/main/kotlin/openai.publish.gradle.kts +++ b/buildSrc/src/main/kotlin/openai.publish.gradle.kts @@ -23,6 +23,31 @@ repositories { mavenCentral() } +// Stable JPMS module names for the published artifacts, derived from the `com.openai` group ID and +// the packages each artifact ships. Without them, consumers on the module path get a name derived +// from the JAR file name, which is neither namespaced nor stable across renames. Treat these as +// public API: once released, changing a name breaks every consumer that `requires` it. +val automaticModuleNames = + mapOf( + // Aggregator artifact: ships no packages of its own, so it takes the group's root name. + "openai-java" to "com.openai", + // Ships `com.openai.core` along with the rest of the `com.openai` namespace. + "openai-java-core" to "com.openai.core", + "openai-java-client-okhttp" to "com.openai.client.okhttp", + "openai-java-bedrock" to "com.openai.bedrock", + ) + +val automaticModuleName = + checkNotNull(automaticModuleNames[project.name]) { + "${project.name} is published but has no automatic module name" + } + +pluginManager.withPlugin("java") { + tasks.named("jar") { + manifest { attributes("Automatic-Module-Name" to automaticModuleName) } + } +} + extra["signingInMemoryKey"] = System.getenv("GPG_SIGNING_KEY") extra["signingInMemoryKeyId"] = System.getenv("GPG_SIGNING_KEY_ID") extra["signingInMemoryKeyPassword"] = System.getenv("GPG_SIGNING_PASSWORD")