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.JsonParseException
7+ import com.google.gson.JsonParser
8+ import okhttp3.Call
9+ import okhttp3.Callback
10+ import okhttp3.Response
11+ import java.io.IOException
12+ import java.util.function.Consumer
13+
14+ internal class OpenAICallback (
15+ private val isStream : Boolean ,
16+ private val onFailure : Consumer <OpenAIError >,
17+ private val onResponse : Consumer <JsonObject >
18+ ) : Callback {
19+
20+ override fun onFailure (call : Call , e : IOException ) {
21+ onFailure.accept(WrappedIOError (e))
22+ }
23+
24+ override fun onResponse (call : Call , response : Response ) {
25+ onResponse(response)
26+ }
27+
28+ fun onResponse (response : Response ) {
29+ if (isStream) {
30+ handleStream(response)
31+ return
32+ }
33+
34+ val rootObject = JsonParser .parseString(response.body!! .string()).asJsonObject
35+
36+ // Sometimes OpenAI will respond with an error code for malformed
37+ // requests, timeouts, rate limits, etc. We need to let the dev
38+ // know that an error occurred.
39+ if (rootObject.has(" error" )) {
40+ onFailure.accept(OpenAIError .fromJson(rootObject.get(" error" ).asJsonObject))
41+ return
42+ }
43+
44+ onResponse.accept(rootObject)
45+ }
46+
47+ private fun handleStream (response : Response ) {
48+ response.body?.source()?.use { source ->
49+
50+ while (! source.exhausted()) {
51+ var jsonResponse = source.readUtf8Line()
52+
53+ // Or data is separated by empty lines, ignore them. The final
54+ // line is always "data: [DONE]", ignore it.
55+ if (jsonResponse.isNullOrEmpty() || jsonResponse == " data: [DONE]" )
56+ continue
57+
58+ // The CHAT API returns a json string, but they prepend the content
59+ // with "data: " (which is not valid json). In order to parse this
60+ // into a JsonObject, we have to strip away this extra string.
61+ if (jsonResponse.startsWith(" data: " ))
62+ jsonResponse = jsonResponse.substring(" data: " .length)
63+
64+ lateinit var rootObject: JsonObject
65+ try {
66+ rootObject = JsonParser .parseString(jsonResponse).asJsonObject
67+ } catch (ex: JsonParseException ) {
68+ println (jsonResponse)
69+ ex.printStackTrace()
70+ continue
71+ }
72+
73+ // Sometimes OpenAI will respond with an error code for malformed
74+ // requests, timeouts, rate limits, etc. We need to let the dev
75+ // know that an error occurred.
76+ if (rootObject.has(" error" )) {
77+ onFailure.accept(OpenAIError .fromJson(rootObject.get(" error" ).asJsonObject))
78+ continue
79+ }
80+
81+ // Developer defined code to run
82+ onResponse.accept(rootObject)
83+ }
84+ }
85+ }
86+ }
0 commit comments