Skip to content

Commit cf97a9a

Browse files
authored
Merge pull request #10 from CJCrafter/exception-wrappers
wrap the json error with Error classes
2 parents 03f40c9 + 707efa4 commit cf97a9a

File tree

10 files changed

+116
-18
lines changed

10 files changed

+116
-18
lines changed

build.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies {
2222
implementation("com.google.code.gson:gson:2.10.1")
2323

2424
testImplementation("io.github.cdimascio:dotenv-kotlin:6.4.1")
25+
testImplementation("org.junit.jupiter:junit-jupiter:5.9.2")
2526
}
2627

2728
java {
@@ -43,6 +44,10 @@ tasks {
4344
}
4445
}
4546

47+
tasks.test {
48+
useJUnitPlatform()
49+
}
50+
4651
// Create javadocJar and sourcesJar tasks
4752
val javadocJar by tasks.registering(Jar::class) {
4853
archiveClassifier.set("javadoc")

src/main/kotlin/com/cjcrafter/openai/OpenAI.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import com.cjcrafter.openai.chat.ChatRequest
44
import com.cjcrafter.openai.chat.ChatResponse
55
import com.cjcrafter.openai.chat.ChatResponseChunk
66
import com.cjcrafter.openai.chat.ChatUser
7+
import com.cjcrafter.openai.exception.OpenAIError
8+
import com.cjcrafter.openai.exception.WrappedIOError
79
import com.google.gson.Gson
810
import com.google.gson.GsonBuilder
911
import com.google.gson.JsonObject
@@ -61,10 +63,9 @@ class OpenAI @JvmOverloads constructor(
6163
*
6264
* @param request The input information for ChatGPT.
6365
* @return The returned response.
64-
* @throws IOException If an IO Exception occurs.
65-
* @throws IllegalArgumentException If the input arguments are invalid.
66+
* @throws OpenAIError Invalid request/timeout/io/etc.
6667
*/
67-
@Throws(IOException::class)
68+
@Throws(OpenAIError::class)
6869
fun createChatCompletion(request: ChatRequest): ChatResponse {
6970
request.stream = false // use streamResponse for stream=true
7071
val httpRequest = buildRequest(request)
@@ -77,11 +78,12 @@ class OpenAI @JvmOverloads constructor(
7778
// Servers respond to API calls with json blocks. Since raw JSON isn't
7879
// very developer friendly, we wrap for easy data access.
7980
rootObject = JsonParser.parseString(response.body!!.string()).asJsonObject
80-
require(!rootObject!!.has("error")) { rootObject!!.get("error").asJsonObject["message"].asString }
81+
if (rootObject!!.has("error"))
82+
throw OpenAIError.fromJson(rootObject!!.get("error").asJsonObject)
8183
return ChatResponse(rootObject!!)
8284
}
83-
} catch (ex: Throwable) {
84-
throw ex
85+
} catch (ex: IOException) {
86+
throw WrappedIOError(ex)
8587
}
8688
}
8789

src/main/kotlin/com/cjcrafter/openai/chat/ChatBot.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
package com.cjcrafter.openai.chat
22

3+
import com.cjcrafter.openai.exception.OpenAIError
34
import com.google.gson.*
45
import okhttp3.*
56
import okhttp3.MediaType.Companion.toMediaType
6-
import okhttp3.OkHttpClient.Builder
77
import okhttp3.RequestBody.Companion.toRequestBody
88
import java.io.IOException
9-
import java.lang.IllegalArgumentException
10-
import java.util.concurrent.TimeUnit
119
import java.util.function.Consumer
1210

1311
/**
@@ -66,8 +64,6 @@ class ChatBot @JvmOverloads constructor(
6664
*
6765
* @param request The input information for ChatGPT.
6866
* @return The returned response.
69-
* @throws IOException If an IO Exception occurs.
70-
* @throws IllegalArgumentException If the input arguments are invalid.
7167
*/
7268
@Throws(IOException::class)
7369
fun generateResponse(request: ChatRequest): ChatResponse {
@@ -79,13 +75,14 @@ class ChatBot @JvmOverloads constructor(
7975
try {
8076
client.newCall(httpRequest).execute().use { response ->
8177

82-
// Servers respond to API calls with json blocks. Since raw JSON isn't
83-
// very developer friendly, we wrap for easy data access.
8478
rootObject = JsonParser.parseString(response.body!!.string()).asJsonObject
85-
require(!rootObject!!.has("error")) { rootObject!!.get("error").asJsonObject["message"].asString }
79+
if (rootObject!!.has("error"))
80+
throw OpenAIError.fromJson(rootObject!!["error"].asJsonObject)
81+
8682
return ChatResponse(rootObject!!)
8783
}
8884
} catch (ex: Throwable) {
85+
println(rootObject)
8986
throw ex
9087
}
9188
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.cjcrafter.openai.exception
2+
3+
import com.google.gson.JsonElement
4+
5+
class InvalidRequestError(param: JsonElement?, code: String?, message: String) : OpenAIError(param, code, message)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.cjcrafter.openai.exception
2+
3+
import com.google.gson.JsonElement
4+
import com.google.gson.JsonObject
5+
6+
// TODO docs
7+
abstract class OpenAIError : Exception {
8+
9+
val param: JsonElement?
10+
val code: String?
11+
12+
constructor(param: JsonElement?, code: String?, message: String) : super(message) {
13+
this.param = param
14+
this.code = code
15+
}
16+
17+
constructor(param: JsonElement?, code: String?, message: String, cause: Throwable?) : super(message, cause) {
18+
this.param = param
19+
this.code = code
20+
}
21+
22+
constructor(param: JsonElement?, code: String?, cause: Throwable?) : super(cause) {
23+
this.param = param
24+
this.code = code
25+
}
26+
27+
companion object {
28+
29+
@JvmStatic
30+
fun fromJson(json: JsonObject) : OpenAIError {
31+
val message = json["message"].asString
32+
val type = if (json["type"].isJsonNull) null else json["type"].asString
33+
val param = if (json["param"].isJsonNull) null else json["param"]
34+
val code = if (json["code"].isJsonNull) null else json["code"].asString
35+
36+
// TODO add more error types
37+
return when (json["type"].asString) {
38+
"invalid_request_error" -> InvalidRequestError(param, code, message)
39+
else -> UnknownError(param, code, message, type)
40+
}
41+
}
42+
}
43+
}

src/main/kotlin/com/cjcrafter/openai/exception/OpenAIException.kt

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.cjcrafter.openai.exception
2+
3+
import com.google.gson.JsonElement
4+
5+
class UnknownError(param: JsonElement?, code: String?, message: String, val type: String?) : OpenAIError(param, code, message)
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.cjcrafter.openai.exception
2+
3+
import java.io.IOException
4+
5+
class WrappedIOError(val exception: IOException) : OpenAIError(null, null, exception.message ?: "")

src/test/java/JavaChatTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import com.cjcrafter.openai.OpenAI;
22
import com.cjcrafter.openai.chat.*;
3+
import com.cjcrafter.openai.exception.OpenAIError;
34
import io.github.cdimascio.dotenv.Dotenv;
45

56
import java.io.IOException;
@@ -9,7 +10,7 @@
910

1011
public class JavaChatTest {
1112

12-
public static void main(String[] args) throws IOException {
13+
public static void main(String[] args) throws OpenAIError {
1314
Scanner scan = new Scanner(System.in);
1415

1516
// This is the prompt that the bot will refer back to for every message.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.cjcrafter.openai;
2+
3+
import com.cjcrafter.openai.chat.*;
4+
import com.cjcrafter.openai.exception.InvalidRequestError;
5+
import io.github.cdimascio.dotenv.Dotenv;
6+
import org.junit.jupiter.api.Test;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
11+
import static org.junit.jupiter.api.Assertions.*;
12+
13+
public class ExceptionTests {
14+
15+
@Test
16+
void test_invalidModel() {
17+
String key = Dotenv.load().get("OPENAI_TOKEN");
18+
19+
String initialPrompt = "Just say hi";
20+
List<ChatMessage> messages = new ArrayList<>(List.of(new ChatMessage(ChatUser.SYSTEM, initialPrompt)));
21+
ChatRequest request = new ChatRequest("gpt-238974-invalid-model", messages);
22+
OpenAI openai = new OpenAI(key);
23+
24+
assertThrows(InvalidRequestError.class, () -> openai.createChatCompletion(request));
25+
}
26+
27+
@Test
28+
void test_invalidToken() {
29+
String key = "sk-Thisisaninvalidtoken";
30+
31+
String initialPrompt = "Just say hi";
32+
List<ChatMessage> messages = new ArrayList<>(List.of(new ChatMessage(ChatUser.SYSTEM, initialPrompt)));
33+
ChatRequest request = new ChatRequest("gpt-3.5-turbo", messages);
34+
OpenAI openai = new OpenAI(key);
35+
36+
assertThrows(InvalidRequestError.class, () -> openai.createChatCompletion(request));
37+
}
38+
}

0 commit comments

Comments
 (0)