Skip to content

Commit f7e1b94

Browse files
authored
feat: Allow for custom Ktor HttpClientPlugin installations (#239)
1 parent 9a7b0fa commit f7e1b94

File tree

6 files changed

+65
-11
lines changed

6 files changed

+65
-11
lines changed

openai-client/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ kotlin {
3131
api(libs.coroutines.core)
3232
api(libs.okio)
3333
implementation(libs.serialization.json)
34-
implementation(libs.ktor.client.core)
34+
api(libs.ktor.client.core)
3535
implementation(libs.ktor.client.logging)
3636
implementation(libs.ktor.client.auth)
3737
implementation(libs.ktor.client.content.negotiation)

openai-client/src/commonMain/kotlin/com.aallam.openai.client/OpenAI.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.aallam.openai.api.http.Timeout
44
import com.aallam.openai.client.internal.OpenAIApi
55
import com.aallam.openai.client.internal.createHttpClient
66
import com.aallam.openai.client.internal.http.HttpTransport
7+
import io.ktor.client.*
78
import kotlin.time.Duration.Companion.seconds
89

910
/**
@@ -24,6 +25,7 @@ public interface OpenAI : Completions, Files, Edits, Embeddings, Models, Moderat
2425
* @param proxy HTTP proxy url
2526
* @param host OpenAI host configuration.
2627
* @param retry rate limit retry configuration
28+
* @param httpClientConfig additional custom client configuration
2729
*/
2830
public fun OpenAI(
2931
token: String,
@@ -34,6 +36,7 @@ public fun OpenAI(
3436
host: OpenAIHost = OpenAIHost.OpenAI,
3537
proxy: ProxyConfig? = null,
3638
retry: RetryStrategy = RetryStrategy(),
39+
httpClientConfig: HttpClientConfig<*>.() -> Unit = {}
3740
): OpenAI = OpenAI(
3841
config = OpenAIConfig(
3942
token = token,
@@ -44,6 +47,7 @@ public fun OpenAI(
4447
host = host,
4548
proxy = proxy,
4649
retry = retry,
50+
httpClientConfig = httpClientConfig,
4751
)
4852
)
4953

openai-client/src/commonMain/kotlin/com.aallam.openai.client/OpenAIConfig.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package com.aallam.openai.client
33
import com.aallam.openai.api.http.Timeout
44
import com.aallam.openai.api.logging.LogLevel
55
import com.aallam.openai.api.logging.Logger
6+
import io.ktor.client.HttpClientConfig
67
import kotlin.time.Duration
78
import kotlin.time.Duration.Companion.seconds
89

@@ -18,6 +19,7 @@ import kotlin.time.Duration.Companion.seconds
1819
* @param proxy HTTP proxy url
1920
* @param host OpenAI host configuration.
2021
* @param retry rate limit retry configuration
22+
* @param httpClientConfig additional custom client configuration
2123
*/
2224
public class OpenAIConfig(
2325
public val token: String,
@@ -28,6 +30,7 @@ public class OpenAIConfig(
2830
public val host: OpenAIHost = OpenAIHost.OpenAI,
2931
public val proxy: ProxyConfig? = null,
3032
public val retry: RetryStrategy = RetryStrategy(),
33+
public val httpClientConfig: HttpClientConfig<*>.() -> Unit = {}
3134
) {
3235

3336
@Deprecated(
@@ -47,6 +50,7 @@ public class OpenAIConfig(
4750
host: OpenAIHost = OpenAIHost.OpenAI,
4851
proxy: ProxyConfig? = null,
4952
retry: RetryStrategy = RetryStrategy(),
53+
httpClientConfig: HttpClientConfig<*>.() -> Unit = {}
5054
) : this(
5155
token = token,
5256
logging = LoggingConfig(
@@ -59,6 +63,7 @@ public class OpenAIConfig(
5963
host = host,
6064
proxy = proxy,
6165
retry = retry,
66+
httpClientConfig = httpClientConfig,
6267
)
6368
}
6469

openai-client/src/commonMain/kotlin/com.aallam.openai.client/internal/HttpClient.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ internal fun createHttpClient(config: OpenAIConfig): HttpClient {
7979
}
8080

8181
expectSuccess = true
82+
83+
config.httpClientConfig(this)
8284
}
8385
}
8486

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.aallam.openai.client
2+
3+
import com.aallam.openai.api.http.Timeout
4+
import io.ktor.client.plugins.api.createClientPlugin
5+
import kotlin.test.Test
6+
import kotlinx.coroutines.test.runTest
7+
import kotlin.test.assertTrue
8+
import kotlin.time.Duration.Companion.minutes
9+
10+
class TestConfigure : TestOpenAI() {
11+
@Test
12+
fun configureClientPlugin() = runTest {
13+
val responseHeaders = mutableListOf<String>()
14+
val plugin = createClientPlugin("CustomHeaderPlugin") {
15+
onResponse { response ->
16+
response.headers.entries().forEach { entry ->
17+
responseHeaders.add(entry.key)
18+
}
19+
}
20+
}
21+
val openAI = generateOpenAI(
22+
OpenAIConfig(
23+
token = token,
24+
timeout = Timeout(socket = 1.minutes)
25+
) {
26+
install(plugin)
27+
}
28+
)
29+
30+
val resModels = openAI.models()
31+
assertTrue { resModels.isNotEmpty() }
32+
assertTrue { responseHeaders.isNotEmpty() }
33+
}
34+
}

openai-client/src/commonTest/kotlin/com/aallam/openai/client/TestOpenAI.kt

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,34 @@ import com.aallam.openai.client.internal.env
88
import com.aallam.openai.client.internal.http.HttpTransport
99
import kotlinx.coroutines.test.TestScope
1010
import kotlinx.coroutines.test.runTest
11-
import okio.Path.Companion.toPath
12-
import okio.Source
1311
import kotlin.time.Duration.Companion.minutes
1412

1513
internal val token: String
1614
get() = requireNotNull(env("OPENAI_API_KEY")) { "OPENAI_API_KEY environment variable must be set." }
1715

18-
internal val transport = HttpTransport(
19-
createHttpClient(
20-
OpenAIConfig(
21-
token = token,
22-
logging = LoggingConfig(logLevel = LogLevel.All),
23-
timeout = Timeout(socket = 1.minutes),
16+
17+
internal val openAIConfig: OpenAIConfig = OpenAIConfig(
18+
token = token,
19+
logging = LoggingConfig(logLevel = LogLevel.All),
20+
timeout = Timeout(socket = 1.minutes),
21+
)
22+
23+
private fun transport(config: OpenAIConfig? = null): HttpTransport {
24+
return HttpTransport(
25+
createHttpClient(
26+
config ?: openAIConfig
2427
)
2528
)
26-
)
29+
}
2730

2831
abstract class TestOpenAI {
29-
internal val openAI = OpenAIApi(transport)
32+
internal val openAI = OpenAIApi(transport())
33+
34+
internal fun generateOpenAI(
35+
config: OpenAIConfig
36+
): OpenAIApi {
37+
return OpenAIApi(transport(config))
38+
}
3039

3140
fun test(testBody: suspend TestScope.() -> Unit) = runTest(timeout = 1.minutes, testBody = testBody)
3241
}

0 commit comments

Comments
 (0)