Skip to content

Commit f99e074

Browse files
committed
Update OpenAI with sync/async methods
1 parent 4a41ef5 commit f99e074

File tree

4 files changed

+272
-176
lines changed

4 files changed

+272
-176
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.cjcrafter.openai
2+
3+
import com.cjcrafter.openai.exception.OpenAIError
4+
import com.cjcrafter.openai.exception.WrappedIOError
5+
import com.google.gson.JsonObject
6+
import com.google.gson.JsonParser
7+
import okhttp3.Call
8+
import okhttp3.Callback
9+
import okhttp3.Response
10+
import java.io.IOException
11+
import java.util.function.Consumer
12+
13+
internal class MyCallback(
14+
private val isStream: Boolean,
15+
private val onFailure: Consumer<OpenAIError>,
16+
private val onResponse: Consumer<JsonObject>
17+
) : Callback {
18+
19+
override fun onFailure(call: Call, e: IOException) {
20+
onFailure.accept(WrappedIOError(e))
21+
}
22+
23+
override fun onResponse(call: Call, response: Response) {
24+
onResponse(response)
25+
}
26+
27+
fun onResponse(response: Response) {
28+
if (isStream) {
29+
handleStream(response)
30+
return
31+
}
32+
33+
val rootObject = JsonParser.parseString(response.body!!.string()).asJsonObject
34+
35+
// Sometimes OpenAI will respond with an error code for malformed
36+
// requests, timeouts, rate limits, etc. We need to let the dev
37+
// know that an error occurred.
38+
if (rootObject.has("error")) {
39+
onFailure.accept(OpenAIError.fromJson(rootObject.get("error").asJsonObject))
40+
return
41+
}
42+
43+
onResponse.accept(rootObject)
44+
}
45+
46+
private fun handleStream(response: Response) {
47+
response.body?.source()?.use { source ->
48+
while (!source.exhausted()) {
49+
var jsonResponse = source.readUtf8()
50+
51+
// OpenAI returns a json string, but they prepend the content with
52+
// "data: " (which is not valid json). In order to parse this into
53+
// a JsonObject, we have to strip away this extra string.
54+
jsonResponse = jsonResponse.substring("data: ".length)
55+
56+
// After OpenAI's final message (which already contains a non-null
57+
// finish reason), they redundantly send "data: [DONE]". Ignore it.
58+
if (jsonResponse == "[DONE]")
59+
continue
60+
61+
val rootObject = JsonParser.parseString(jsonResponse).asJsonObject
62+
63+
// Sometimes OpenAI will respond with an error code for malformed
64+
// requests, timeouts, rate limits, etc. We need to let the dev
65+
// know that an error occurred.
66+
if (rootObject.has("error")) {
67+
onFailure.accept(OpenAIError.fromJson(rootObject.get("error").asJsonObject))
68+
continue
69+
}
70+
71+
// Developer defined code to run
72+
onResponse.accept(rootObject)
73+
}
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)