Skip to content

Commit

Permalink
feat: initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
stainless-app[bot] authored and meorphis committed Nov 8, 2024
1 parent f4fc7a5 commit fa016ee
Show file tree
Hide file tree
Showing 13 changed files with 13 additions and 294 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
package com.openai.client.okhttp

import com.fasterxml.jackson.databind.json.JsonMapper
import com.openai.azure.AzureOpenAIServiceVersion
import com.openai.client.OpenAIClient
import com.openai.client.OpenAIClientImpl
import com.openai.core.ClientOptions
import com.openai.core.http.Headers
import com.openai.core.http.QueryParams
import com.openai.credential.Credential
import java.net.Proxy
import java.time.Clock
import java.time.Duration
Expand Down Expand Up @@ -132,12 +130,6 @@ class OpenAIOkHttpClient private constructor() {

fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }

fun credential(credential: Credential) = apply { clientOptions.credential(credential) }

fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply {
clientOptions.azureServiceVersion(azureServiceVersion)
}

fun organization(organization: String?) = apply { clientOptions.organization(organization) }

fun project(project: String?) = apply { clientOptions.project(project) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@
package com.openai.client.okhttp

import com.fasterxml.jackson.databind.json.JsonMapper
import com.openai.azure.AzureOpenAIServiceVersion
import com.openai.client.OpenAIClientAsync
import com.openai.client.OpenAIClientAsyncImpl
import com.openai.core.ClientOptions
import com.openai.core.http.Headers
import com.openai.core.http.QueryParams
import com.openai.credential.Credential
import java.net.Proxy
import java.time.Clock
import java.time.Duration
Expand Down Expand Up @@ -132,12 +130,6 @@ class OpenAIOkHttpClientAsync private constructor() {

fun apiKey(apiKey: String) = apply { clientOptions.apiKey(apiKey) }

fun credential(credential: Credential) = apply { clientOptions.credential(credential) }

fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply {
clientOptions.azureServiceVersion(azureServiceVersion)
}

fun organization(organization: String?) = apply { clientOptions.organization(organization) }

fun project(project: String?) = apply { clientOptions.project(project) }
Expand Down

This file was deleted.

This file was deleted.

80 changes: 12 additions & 68 deletions openai-java-core/src/main/kotlin/com/openai/core/ClientOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@
package com.openai.core

import com.fasterxml.jackson.databind.json.JsonMapper
import com.openai.azure.AzureOpenAIServiceVersion
import com.openai.azure.AzureOpenAIServiceVersion.Companion.V2024_06_01
import com.openai.azure.credential.AzureApiKeyCredential
import com.openai.core.http.Headers
import com.openai.core.http.HttpClient
import com.openai.core.http.PhantomReachableClosingHttpClient
import com.openai.core.http.QueryParams
import com.openai.core.http.RetryingHttpClient
import com.openai.credential.BearerTokenCredential
import com.openai.credential.Credential
import java.time.Clock

class ClientOptions
Expand All @@ -26,7 +21,7 @@ private constructor(
@get:JvmName("queryParams") val queryParams: QueryParams,
@get:JvmName("responseValidation") val responseValidation: Boolean,
@get:JvmName("maxRetries") val maxRetries: Int,
@get:JvmName("credential") val credential: Credential,
@get:JvmName("apiKey") val apiKey: String,
@get:JvmName("organization") val organization: String?,
@get:JvmName("project") val project: String?,
) {
Expand All @@ -52,8 +47,7 @@ private constructor(
private var queryParams: QueryParams.Builder = QueryParams.builder()
private var responseValidation: Boolean = false
private var maxRetries: Int = 2
private var credential: Credential? = null
private var azureServiceVersion: AzureOpenAIServiceVersion? = null
private var apiKey: String? = null
private var organization: String? = null
private var project: String? = null

Expand All @@ -67,7 +61,7 @@ private constructor(
queryParams = clientOptions.queryParams.toBuilder()
responseValidation = clientOptions.responseValidation
maxRetries = clientOptions.maxRetries
credential = clientOptions.credential
apiKey = clientOptions.apiKey
organization = clientOptions.organization
project = clientOptions.project
}
Expand Down Expand Up @@ -166,56 +160,21 @@ private constructor(

fun maxRetries(maxRetries: Int) = apply { this.maxRetries = maxRetries }

fun apiKey(apiKey: String) = apply {
this.credential = BearerTokenCredential.create(apiKey)
}

fun credential(credential: Credential) = apply { this.credential = credential }

fun azureServiceVersion(azureServiceVersion: AzureOpenAIServiceVersion) = apply {
this.azureServiceVersion = azureServiceVersion
}
fun apiKey(apiKey: String) = apply { this.apiKey = apiKey }

fun organization(organization: String?) = apply { this.organization = organization }

fun project(project: String?) = apply { this.project = project }

fun fromEnv() = apply {
val openAIKey = System.getenv("OPENAI_API_KEY")
val openAIOrgId = System.getenv("OPENAI_ORG_ID")
val openAIProjectId = System.getenv("OPENAI_PROJECT_ID")
val azureOpenAIKey = System.getenv("AZURE_OPENAI_KEY")
val azureEndpoint = System.getenv("AZURE_OPENAI_ENDPOINT")

when {
!openAIKey.isNullOrEmpty() && !azureOpenAIKey.isNullOrEmpty() -> {
throw IllegalArgumentException(
"Both OpenAI and Azure OpenAI API keys, `OPENAI_API_KEY` and `AZURE_OPENAI_KEY`, are set. Please specify only one"
)
}
!openAIKey.isNullOrEmpty() -> {
credential(BearerTokenCredential.create(openAIKey))
organization(openAIOrgId)
project(openAIProjectId)
}
!azureOpenAIKey.isNullOrEmpty() -> {
credential(AzureApiKeyCredential.create(azureOpenAIKey))
baseUrl(azureEndpoint)
}
!azureEndpoint.isNullOrEmpty() -> {
// Both 'openAIKey' and 'azureOpenAIKey' are not set.
// Only 'azureEndpoint' is set here, and user still needs to call method
// '.credential(BearerTokenCredential(Supplier<String>))'
// to get the token through the supplier, which requires Azure Entra ID as a
// dependency.
baseUrl(azureEndpoint)
}
}
System.getenv("OPENAI_API_KEY")?.let { apiKey(it) }
System.getenv("OPENAI_ORG_ID")?.let { organization(it) }
System.getenv("OPENAI_PROJECT_ID")?.let { project(it) }
}

fun build(): ClientOptions {
checkNotNull(httpClient) { "`httpClient` is required but was not set" }
checkNotNull(credential) { "`credential` is required but was not set" }
checkNotNull(apiKey) { "`apiKey` is required but was not set" }

val headers = Headers.builder()
val queryParams = QueryParams.builder()
Expand All @@ -228,26 +187,11 @@ private constructor(
headers.put("X-Stainless-Runtime-Version", getJavaVersion())
organization?.let { headers.put("OpenAI-Organization", it) }
project?.let { headers.put("OpenAI-Project", it) }

when (val currentCredential = credential) {
is AzureApiKeyCredential -> {
headers.put("api-key", currentCredential.apiKey())
}
is BearerTokenCredential -> {
headers.put("Authorization", "Bearer ${currentCredential.token()}")
}
else -> {
throw IllegalArgumentException("Invalid credential type")
apiKey?.let {
if (!it.isEmpty()) {
headers.put("Authorization", "Bearer $it")
}
}

if (isAzureEndpoint(baseUrl)) {
// Default Azure OpenAI version is used if Azure user doesn't
// specific a service API version in 'queryParams'.
// We can update the default value every major announcement if needed.
replaceQueryParams("api-version", (azureServiceVersion ?: V2024_06_01).value)
}

headers.replaceAll(this.headers.build())
queryParams.replaceAll(this.queryParams.build())

Expand All @@ -267,7 +211,7 @@ private constructor(
queryParams.build(),
responseValidation,
maxRetries,
credential!!,
apiKey!!,
organization,
project,
)
Expand Down
7 changes: 0 additions & 7 deletions openai-java-core/src/main/kotlin/com/openai/core/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,4 @@ internal fun <K : Comparable<K>, V> SortedMap<K, V>.toImmutable(): SortedMap<K,
if (isEmpty()) Collections.emptySortedMap()
else Collections.unmodifiableSortedMap(toSortedMap(comparator()))

@JvmSynthetic
internal fun isAzureEndpoint(baseUrl: String): Boolean {
// Azure Endpoint should be in the format of `https://<region>.openai.azure.com`.
// Or `https://<region>.azure-api.net` for Azure OpenAI Management URL.
return baseUrl.endsWith(".openai.azure.com", true) || baseUrl.endsWith(".azure-api.net", true)
}

internal interface Enum

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ import com.openai.core.handlers.withErrorHandler
import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.isAzureEndpoint
import com.openai.core.json
import com.openai.credential.BearerTokenCredential
import com.openai.errors.OpenAIError
import com.openai.models.ChatCompletion
import com.openai.models.ChatCompletionCreateParams
Expand Down Expand Up @@ -41,23 +39,10 @@ constructor(
val request =
HttpRequest.builder()
.method(HttpMethod.POST)
.apply {
if (isAzureEndpoint(clientOptions.baseUrl)) {
addPathSegments("openai", "deployments", params.model().toString())
}
}
.addPathSegments("chat", "completions")
.putAllQueryParams(clientOptions.queryParams)
.replaceAllQueryParams(params.getQueryParams())
.putAllHeaders(clientOptions.headers)
.apply {
if (
isAzureEndpoint(clientOptions.baseUrl) &&
clientOptions.credential is BearerTokenCredential
) {
putHeader("Authorization", "Bearer ${clientOptions.credential.token()}")
}
}
.replaceAllHeaders(params.getHeaders())
.body(json(clientOptions.jsonMapper, params.getBody()))
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@ import com.openai.core.http.HttpMethod
import com.openai.core.http.HttpRequest
import com.openai.core.http.HttpResponse.Handler
import com.openai.core.http.StreamResponse
import com.openai.core.isAzureEndpoint
import com.openai.core.json
import com.openai.credential.BearerTokenCredential
import com.openai.errors.OpenAIError
import com.openai.models.ChatCompletion
import com.openai.models.ChatCompletionChunk
Expand Down Expand Up @@ -46,23 +44,10 @@ constructor(
val request =
HttpRequest.builder()
.method(HttpMethod.POST)
.apply {
if (isAzureEndpoint(clientOptions.baseUrl)) {
addPathSegments("openai", "deployments", params.model().toString())
}
}
.addPathSegments("chat", "completions")
.putAllQueryParams(clientOptions.queryParams)
.replaceAllQueryParams(params.getQueryParams())
.putAllHeaders(clientOptions.headers)
.apply {
if (
isAzureEndpoint(clientOptions.baseUrl) &&
clientOptions.credential is BearerTokenCredential
) {
putHeader("Authorization", "Bearer ${clientOptions.credential.token()}")
}
}
.replaceAllHeaders(params.getHeaders())
.body(json(clientOptions.jsonMapper, params.getBody()))
.build()
Expand Down
Loading

0 comments on commit fa016ee

Please sign in to comment.