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