commonMain.com.aallam.openai.client.internal.HttpClient.kt Maven / Gradle / Ivy
package com.aallam.openai.client.internal
import com.aallam.openai.client.OpenAIConfig
import com.aallam.openai.client.ProxyConfig
import com.aallam.openai.client.internal.extension.toKtorLogLevel
import com.aallam.openai.client.internal.extension.toKtorLogger
import io.ktor.client.*
import io.ktor.client.engine.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.plugins.logging.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.*
import io.ktor.util.*
import kotlinx.serialization.json.Json
import kotlin.time.DurationUnit
/**
* Default Http Client.
*/
internal fun createHttpClient(config: OpenAIConfig): HttpClient {
val configuration: HttpClientConfig<*>.() -> Unit = {
engine {
config.proxy?.let { proxyConfig ->
proxy = when (proxyConfig) {
is ProxyConfig.Http -> ProxyBuilder.http(proxyConfig.url)
is ProxyConfig.Socks -> ProxyBuilder.socks(proxyConfig.host, proxyConfig.port)
}
}
}
install(ContentNegotiation) {
register(ContentType.Application.Json, KotlinxSerializationConverter(JsonLenient))
}
install(Logging) {
val logging = config.logging
logger = logging.logger.toKtorLogger()
level = logging.logLevel.toKtorLogLevel()
if (logging.sanitize) {
sanitizeHeader { header -> header == HttpHeaders.Authorization }
}
}
install(Auth) {
bearer {
loadTokens {
BearerTokens(accessToken = config.token, refreshToken = "")
}
}
}
install(HttpTimeout) {
config.timeout.socket?.let { socketTimeout ->
socketTimeoutMillis = socketTimeout.toLong(DurationUnit.MILLISECONDS)
}
config.timeout.connect?.let { connectTimeout ->
connectTimeoutMillis = connectTimeout.toLong(DurationUnit.MILLISECONDS)
}
config.timeout.request?.let { requestTimeout ->
requestTimeoutMillis = requestTimeout.toLong(DurationUnit.MILLISECONDS)
}
}
install(HttpRequestRetry) {
maxRetries = config.retry.maxRetries
// retry on rate limit error.
retryIf { _, response -> response.status.value.let { it == 429 } }
exponentialDelay(config.retry.base, config.retry.maxDelay.inWholeMilliseconds)
}
defaultRequest {
url(config.host.baseUrl)
config.host.queryParams.onEach { (key, value) -> url.parameters.appendIfNameAbsent(key, value) }
config.organization?.let { organization -> headers.append("OpenAI-Organization", organization) }
config.headers.onEach { (key, value) -> headers.appendIfNameAbsent(key, value) }
}
expectSuccess = true
config.httpClientConfig(this)
}
return if(config.engine != null) {
HttpClient(config.engine, configuration)
} else {
HttpClient(configuration)
}
}
/**
* Internal Json Serializer.
*/
internal val JsonLenient = Json {
isLenient = true
ignoreUnknownKeys = true
}