Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve error reporting when things fail #105

Merged
merged 3 commits into from
May 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.xebia.functional.xef.llm

import kotlinx.serialization.json.JsonElement

data class AIClientError(val json: JsonElement) : Exception("AI client error")
Original file line number Diff line number Diff line change
@@ -5,17 +5,21 @@ import arrow.resilience.retry
import com.xebia.functional.xef.configure
import com.xebia.functional.xef.env.OpenAIConfig
import com.xebia.functional.xef.httpClient
import com.xebia.functional.xef.llm.AIClientError
import io.github.oshai.KLogger
import io.github.oshai.KotlinLogging
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.engine.HttpClientEngine
import io.ktor.client.plugins.timeout
import io.ktor.client.request.post
import io.ktor.client.statement.HttpResponse
import io.ktor.client.statement.*
import io.ktor.http.path
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement

private val logger: KLogger = KotlinLogging.logger {}

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

val body: CompletionResult = response.body()
val body: CompletionResult = response.bodyOrError()
with(body.usage) {
logger.debug {
"Completion Tokens :: prompt: $promptTokens, completion: $completionTokens, total: $totalTokens"
@@ -85,7 +89,7 @@ private class KtorOpenAIClient(
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
}
}
val body: ChatCompletionResponse = response.body()
val body: ChatCompletionResponse = response.bodyOrError()
with(body.usage) {
logger.debug {
"Chat Completion Tokens :: prompt: $promptTokens, completion: $completionTokens, total: $totalTokens"
@@ -103,7 +107,7 @@ private class KtorOpenAIClient(
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
}
}
val body: EmbeddingResult = response.body()
val body: EmbeddingResult = response.bodyOrError()
with(body.usage) { logger.debug { "Embeddings Tokens :: total: $totalTokens" } }
return body
}
@@ -117,6 +121,20 @@ private class KtorOpenAIClient(
timeout { requestTimeoutMillis = config.requestTimeout.inWholeMilliseconds }
}
}
return response.body()
return response.bodyOrError()
}
}

val JsonLenient = Json {
isLenient = true
ignoreUnknownKeys = true
}

private suspend inline fun <reified T> HttpResponse.bodyOrError(): T {
val contents = bodyAsText()
try {
return JsonLenient.decodeFromString<T>(contents)
} catch (_: IllegalArgumentException) {
throw AIClientError(JsonLenient.decodeFromString<JsonElement>(contents))
}
}