Skip to content

Commit aefb677

Browse files
authored
Improve error reporting when things fail (#105)
* Improve error reporting when things fail * Missing file * Spotless
1 parent f32004d commit aefb677

File tree

2 files changed

+28
-5
lines changed

2 files changed

+28
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package com.xebia.functional.xef.llm
2+
3+
import kotlinx.serialization.json.JsonElement
4+
5+
data class AIClientError(val json: JsonElement) : Exception("AI client error")

core/src/commonMain/kotlin/com/xebia/functional/xef/llm/openai/OpenAIClient.kt

+23-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,21 @@ import arrow.resilience.retry
55
import com.xebia.functional.xef.configure
66
import com.xebia.functional.xef.env.OpenAIConfig
77
import com.xebia.functional.xef.httpClient
8+
import com.xebia.functional.xef.llm.AIClientError
89
import io.github.oshai.KLogger
910
import io.github.oshai.KotlinLogging
1011
import io.ktor.client.HttpClient
1112
import io.ktor.client.call.body
1213
import io.ktor.client.engine.HttpClientEngine
1314
import io.ktor.client.plugins.timeout
1415
import io.ktor.client.request.post
15-
import io.ktor.client.statement.HttpResponse
16+
import io.ktor.client.statement.*
1617
import io.ktor.http.path
1718
import kotlinx.serialization.SerialName
1819
import kotlinx.serialization.Serializable
20+
import kotlinx.serialization.decodeFromString
21+
import kotlinx.serialization.json.Json
22+
import kotlinx.serialization.json.JsonElement
1923

2024
private val logger: KLogger = KotlinLogging.logger {}
2125

@@ -60,7 +64,7 @@ private class KtorOpenAIClient(
6064
}
6165
}
6266

63-
val body: CompletionResult = response.body()
67+
val body: CompletionResult = response.bodyOrError()
6468
with(body.usage) {
6569
logger.debug {
6670
"Completion Tokens :: prompt: $promptTokens, completion: $completionTokens, total: $totalTokens"
@@ -85,7 +89,7 @@ private class KtorOpenAIClient(
8589
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
8690
}
8791
}
88-
val body: ChatCompletionResponse = response.body()
92+
val body: ChatCompletionResponse = response.bodyOrError()
8993
with(body.usage) {
9094
logger.debug {
9195
"Chat Completion Tokens :: prompt: $promptTokens, completion: $completionTokens, total: $totalTokens"
@@ -103,7 +107,7 @@ private class KtorOpenAIClient(
103107
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
104108
}
105109
}
106-
val body: EmbeddingResult = response.body()
110+
val body: EmbeddingResult = response.bodyOrError()
107111
with(body.usage) { logger.debug { "Embeddings Tokens :: total: $totalTokens" } }
108112
return body
109113
}
@@ -117,6 +121,20 @@ private class KtorOpenAIClient(
117121
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
118122
}
119123
}
120-
return response.body()
124+
return response.bodyOrError()
125+
}
126+
}
127+
128+
val JsonLenient = Json {
129+
isLenient = true
130+
ignoreUnknownKeys = true
131+
}
132+
133+
private suspend inline fun <reified T> HttpResponse.bodyOrError(): T {
134+
val contents = bodyAsText()
135+
try {
136+
return JsonLenient.decodeFromString<T>(contents)
137+
} catch (_: IllegalArgumentException) {
138+
throw AIClientError(JsonLenient.decodeFromString<JsonElement>(contents))
121139
}
122140
}

0 commit comments

Comments
 (0)