diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index d87cca6..5f3ea9b 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.11.5"
+ ".": "0.11.6"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3db45e1..d4b8e3c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,14 @@
# Changelog
+## 0.11.6 (2025-01-17)
+
+Full Changelog: [v0.11.5...v0.11.6](https://github.com/openai/openai-java/compare/v0.11.5...v0.11.6)
+
+### Chores
+
+* **internal:** move `StreamResponse` method ([#131](https://github.com/openai/openai-java/issues/131)) ([5888e39](https://github.com/openai/openai-java/commit/5888e398597b68a520b4bb3953d96403023331fd))
+* **internal:** refactor streaming implementation ([#129](https://github.com/openai/openai-java/issues/129)) ([d2831ec](https://github.com/openai/openai-java/commit/d2831ec587d37a2c2d51332f5f2f62ba3dd1181d))
+
## 0.11.5 (2025-01-16)
Full Changelog: [v0.11.4...v0.11.5](https://github.com/openai/openai-java/compare/v0.11.4...v0.11.5)
diff --git a/README.md b/README.md
index f960fb5..443cc82 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@
-[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.11.5)
+[![Maven Central](https://img.shields.io/maven-central/v/com.openai/openai-java)](https://central.sonatype.com/artifact/com.openai/openai-java/0.11.6)
@@ -30,7 +30,7 @@ The REST API documentation can be foundĀ on [platform.openai.com](https://platfo
```kotlin
-implementation("com.openai:openai-java:0.11.5")
+implementation("com.openai:openai-java:0.11.6")
```
#### Maven
@@ -39,7 +39,7 @@ implementation("com.openai:openai-java:0.11.5")
com.openai
openai-java
- 0.11.5
+ 0.11.6
```
diff --git a/build.gradle.kts b/build.gradle.kts
index e06eb10..071e5e9 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -8,7 +8,7 @@ repositories {
allprojects {
group = "com.openai"
- version = "0.11.5" // x-release-please-version
+ version = "0.11.6" // x-release-please-version
}
subprojects {
diff --git a/openai-java-core/src/main/kotlin/com/openai/core/handlers/SseHandler.kt b/openai-java-core/src/main/kotlin/com/openai/core/handlers/SseHandler.kt
index f04c2cf..a5dd257 100644
--- a/openai-java-core/src/main/kotlin/com/openai/core/handlers/SseHandler.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/core/handlers/SseHandler.kt
@@ -6,69 +6,40 @@ import com.fasterxml.jackson.databind.json.JsonMapper
import com.openai.core.JsonValue
import com.openai.core.http.HttpResponse
import com.openai.core.http.HttpResponse.Handler
-import com.openai.core.http.PhantomReachableClosingStreamResponse
import com.openai.core.http.SseMessage
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.errors.OpenAIException
-import java.util.stream.Stream
import kotlin.jvm.optionals.getOrNull
-import kotlin.streams.asStream
@JvmSynthetic
internal fun sseHandler(jsonMapper: JsonMapper): Handler> =
- object : Handler> {
-
- override fun handle(response: HttpResponse): StreamResponse {
- val reader = response.body().bufferedReader()
- val sequence =
- sequence {
- reader.useLines { lines ->
- val state = SseState(jsonMapper)
- var done = false
- for (line in lines) {
- // Stop emitting messages, but iterate through the full stream.
- if (done) {
- continue
- }
- val message = state.decode(line) ?: continue
-
- if (message.data.startsWith("[DONE]")) {
- // In this case we don't break because we still want to iterate
- // through the full stream.
- done = true
- continue
- }
-
- val error =
- message.json().asObject().getOrNull()?.get("error")
- if (error != null) {
- val errorMessage =
- error.asString().getOrNull()
- ?: error
- .asObject()
- .getOrNull()
- ?.get("message")
- ?.asString()
- ?.getOrNull()
- ?: "An error occurred during streaming"
- throw OpenAIException(errorMessage)
- }
- yield(message)
- }
- }
- }
- .constrainOnce()
-
- return PhantomReachableClosingStreamResponse(
- object : StreamResponse {
- override fun stream(): Stream = sequence.asStream()
-
- override fun close() {
- reader.close()
- response.close()
- }
- }
- )
+ streamHandler { lines ->
+ val state = SseState(jsonMapper)
+ var done = false
+ for (line in lines) {
+ // Stop emitting messages, but iterate through the full stream.
+ if (done) {
+ continue
+ }
+ val message = state.decode(line) ?: continue
+
+ if (message.data.startsWith("[DONE]")) {
+ // In this case we don't break because we still want to iterate through the full
+ // stream.
+ done = true
+ continue
+ }
+
+ val error = message.json().asObject().getOrNull()?.get("error")
+ if (error != null) {
+ val errorMessage =
+ error.asString().getOrNull()
+ ?: error.asObject().getOrNull()?.get("message")?.asString()?.getOrNull()
+ ?: "An error occurred during streaming"
+ throw OpenAIException(errorMessage)
+ }
+ yield(message)
}
}
@@ -158,11 +129,3 @@ internal inline fun Handler>.mapJson():
}
}
}
-
-@JvmSynthetic
-internal fun StreamResponse.map(transform: (T) -> R): StreamResponse =
- object : StreamResponse {
- override fun stream(): Stream = this@map.stream().map(transform)
-
- override fun close() = this@map.close()
- }
diff --git a/openai-java-core/src/main/kotlin/com/openai/core/handlers/StreamHandler.kt b/openai-java-core/src/main/kotlin/com/openai/core/handlers/StreamHandler.kt
new file mode 100644
index 0000000..27040d5
--- /dev/null
+++ b/openai-java-core/src/main/kotlin/com/openai/core/handlers/StreamHandler.kt
@@ -0,0 +1,32 @@
+@file:JvmName("StreamHandler")
+
+package com.openai.core.handlers
+
+import com.openai.core.http.HttpResponse
+import com.openai.core.http.HttpResponse.Handler
+import com.openai.core.http.PhantomReachableClosingStreamResponse
+import com.openai.core.http.StreamResponse
+import java.util.stream.Stream
+import kotlin.streams.asStream
+
+@JvmSynthetic
+internal fun streamHandler(
+ block: suspend SequenceScope.(lines: Sequence) -> Unit
+): Handler> =
+ object : Handler> {
+ override fun handle(response: HttpResponse): StreamResponse {
+ val reader = response.body().bufferedReader()
+ val sequence = sequence { reader.useLines { block(it) } }.constrainOnce()
+
+ return PhantomReachableClosingStreamResponse(
+ object : StreamResponse {
+ override fun stream(): Stream = sequence.asStream()
+
+ override fun close() {
+ reader.close()
+ response.close()
+ }
+ }
+ )
+ }
+ }
diff --git a/openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt b/openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt
index 1a41bb8..c7c5e63 100644
--- a/openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/core/http/StreamResponse.kt
@@ -6,3 +6,11 @@ interface StreamResponse : AutoCloseable {
fun stream(): Stream
}
+
+@JvmSynthetic
+internal fun StreamResponse.map(transform: (T) -> R): StreamResponse =
+ object : StreamResponse {
+ override fun stream(): Stream = this@map.stream().map(transform)
+
+ override fun close() = this@map.close()
+ }
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt
index 53c0e8f..060946d 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/async/CompletionServiceAsyncImpl.kt
@@ -9,7 +9,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -18,6 +17,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.http.toAsync
import com.openai.core.json
import com.openai.errors.OpenAIError
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt
index 939b47f..95d8a2f 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/ThreadServiceAsyncImpl.kt
@@ -7,7 +7,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -16,6 +15,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.http.toAsync
import com.openai.core.json
import com.openai.errors.OpenAIError
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt
index 2fc1b4d..65a1cb9 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/async/beta/threads/RunServiceAsyncImpl.kt
@@ -7,7 +7,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -16,6 +15,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.http.toAsync
import com.openai.core.json
import com.openai.errors.OpenAIError
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt
index 6482c72..7114e93 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/async/chat/CompletionServiceAsyncImpl.kt
@@ -9,7 +9,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -18,6 +17,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.http.toAsync
import com.openai.core.json
import com.openai.errors.OpenAIError
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt
index a839a43..77edafc 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/CompletionServiceImpl.kt
@@ -9,7 +9,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -17,6 +16,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.json
import com.openai.errors.OpenAIError
import com.openai.models.Completion
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt
index d081660..533391e 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/ThreadServiceImpl.kt
@@ -7,7 +7,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -15,6 +14,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.json
import com.openai.errors.OpenAIError
import com.openai.models.AssistantStreamEvent
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt
index d29f20a..c0fb5e3 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/beta/threads/RunServiceImpl.kt
@@ -7,7 +7,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -15,6 +14,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.json
import com.openai.errors.OpenAIError
import com.openai.models.AssistantStreamEvent
diff --git a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt
index 1653947..7a362b8 100644
--- a/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt
+++ b/openai-java-core/src/main/kotlin/com/openai/services/blocking/chat/CompletionServiceImpl.kt
@@ -9,7 +9,6 @@ import com.openai.core.JsonValue
import com.openai.core.RequestOptions
import com.openai.core.handlers.errorHandler
import com.openai.core.handlers.jsonHandler
-import com.openai.core.handlers.map
import com.openai.core.handlers.mapJson
import com.openai.core.handlers.sseHandler
import com.openai.core.handlers.withErrorHandler
@@ -17,6 +16,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
+import com.openai.core.http.map
import com.openai.core.json
import com.openai.errors.OpenAIError
import com.openai.models.ChatCompletion