Skip to content

Commit

Permalink
chore: support multiple native targets (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
aallam authored Mar 8, 2022
1 parent 1c046f3 commit 4468e12
Show file tree
Hide file tree
Showing 31 changed files with 207 additions and 156 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,7 @@ coverage-error.log
.DS_Store

# Java
*.hprof
*.hprof

# kmp
kotlin-js-store
22 changes: 22 additions & 0 deletions build-support/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
plugins {
`kotlin-dsl`
`java-gradle-plugin`
}

repositories {
mavenCentral()
}

dependencies {
compileOnly(kotlin("gradle-plugin"))
compileOnly(kotlin("gradle-plugin-api"))
}

gradlePlugin {
plugins {
create("build-support") {
id = "build-support"
implementationClass = "BuildSupport"
}
}
}
1 change: 1 addition & 0 deletions build-support/settings.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// empty.
8 changes: 8 additions & 0 deletions build-support/src/main/kotlin/BuildSupport.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import org.gradle.api.Plugin
import org.gradle.api.Project

class BuildSupport : Plugin<Project> {
override fun apply(project: Project) {
// Do nothing.
}
}
21 changes: 21 additions & 0 deletions build-support/src/main/kotlin/Platforms.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import org.gradle.kotlin.dsl.creating
import org.gradle.kotlin.dsl.getValue
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

fun KotlinMultiplatformExtension.native() {
val targets = mutableListOf<KotlinNativeTarget>().apply {
add(linuxX64())
add(macosX64())
add(macosArm64())
add(mingwX64())
}
sourceSets.apply {
val nativeMain by creating { dependsOn(getByName("commonMain")) }
val nativeTest by creating { dependsOn(getByName("commonTest")) }
targets.forEach { target ->
getByName("${target.name}Main").dependsOn(nativeMain)
getByName("${target.name}Test").dependsOn(nativeTest)
}
}
}
27 changes: 22 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,19 +1,36 @@
import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.gradle.api.tasks.testing.logging.TestLogEvent.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

buildscript {
repositories {
mavenCentral()
}
dependencies {
val kotlinVersion = "1.5.10"
val kotlinVersion = "1.6.10"
classpath(kotlin("gradle-plugin", version = kotlinVersion))
classpath(kotlin("serialization", version = kotlinVersion))
classpath("com.vanniktech:gradle-maven-publish-plugin:0.15.1")
classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.6.0")
classpath("com.vanniktech:gradle-maven-publish-plugin:0.18.0")
classpath("org.jetbrains.kotlinx:binary-compatibility-validator:0.8.0")
}
}

subprojects {
allprojects {
repositories {
mavenCentral()
}
val javadoc by tasks.creating(Javadoc::class)

tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}

tasks.withType<Test> {
testLogging {
events(STARTED, PASSED, SKIPPED, FAILED)
exceptionFormat = TestExceptionFormat.FULL
showStandardStreams = false
}
}
}
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
kotlin.code.style=official
kotlin.mpp.stability.nowarn=true
kotlin.mpp.enableGranularSourceSetsMetadata=true
kotlin.mpp.enableCompatibilityMetadataVariant=true
kotlin.native.enableDependencyPropagation=false
kotlin.js.generate.executable.default=false

# Lib
GROUP=com.aallam.openai
Expand Down
19 changes: 11 additions & 8 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
[versions]
coroutines = "1.5.0-native-mt"
serialization = "1.2.1"
ktor = "1.6.0"
okio = "3.0.0-alpha.6"
coroutines = "1.6.0-native-mt"
serialization = "1.3.1"
ktor = "2.0.0-beta-1"
okio = "3.0.0"
logback = "1.2.3"

[libraries]
# Coroutines
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
# Serialization
serialization-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-core", version.ref = "serialization" }
serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "serialization" }
# Ktor
ktor-client-json = { group = "io.ktor", name = "ktor-client-json", version.ref = "ktor" }
ktor-client-logging = { group = "io.ktor", name = "ktor-client-logging", version.ref = "ktor" }
ktor-client-serialization = { group = "io.ktor", name = "ktor-client-serialization", version.ref = "ktor" }
ktor-client-okhttp = { group = "io.ktor", name = "ktor-client-okhttp", version.ref = "ktor" }
ktor-client-curl = { group = "io.ktor", name = "ktor-client-curl", version.ref = "ktor" }
ktor-client-apache = { group = "io.ktor", name = "ktor-client-apache", version.ref = "ktor" }
ktor-client-auth = { group = "io.ktor", name = "ktor-client-auth", version.ref = "ktor" }
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
ktor-client-serialization-kotlinx = { group = "io.ktor", name = "ktor-serialization-kotlinx", version.ref = "ktor" }
# Okio
okio-multiplatform = { group = "com.squareup.okio", name = "okio-multiplatform", version.ref = "okio" }
okio-nodefilesystem = { group = "com.squareup.okio", name = "okio-nodefilesystem-multiplatform", version.ref = "okio" }
okio-fakefilesystem = { group = "com.squareup.okio", name = "okio-fakefilesystem-multiplatform", version.ref = "okio" }
okio = { group = "com.squareup.okio", name = "okio", version.ref = "okio" }
okio-nodefilesystem = { group = "com.squareup.okio", name = "okio-nodefilesystem", version.ref = "okio" }
okio-fakefilesystem = { group = "com.squareup.okio", name = "okio-fakefilesystem", version.ref = "okio" }
# Logback
logback-classic = { group = "ch.qos.logback", name = "logback-classic", version.ref = "logback" }
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
43 changes: 14 additions & 29 deletions openai-client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,56 +3,42 @@ plugins {
kotlin("plugin.serialization")
id("com.vanniktech.maven.publish")
id("binary-compatibility-validator")
id("build-support")
}

kotlin {
explicitApi()
jvm {
compilations.all {
kotlinOptions.jvmTarget = "1.8"
}
testRuns["test"].executionTask.configure {
useJUnit()
}
}
js(LEGACY) {
nodejs()
}

val hostOs = System.getProperty("os.name")
val isMingwX64 = hostOs.startsWith("Windows")
val nativeTarget = when {
hostOs == "Mac OS X" -> macosX64("native")
hostOs == "Linux" -> linuxX64("native")
isMingwX64 -> mingwX64("native")
else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
}
nativeTarget.apply {
binaries {}
}
jvm()
js { nodejs() }
native()

sourceSets {
all {
languageSettings.apply {
useExperimentalAnnotation("kotlin.RequiresOptIn")
useExperimentalAnnotation("okio.ExperimentalFileSystem")
languageSettings {
optIn("kotlin.RequiresOptIn")
optIn("okio.ExperimentalFileSystem")
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
}
}
val commonMain by getting {
dependencies {
api(project(":openai-core"))
api(libs.coroutines.core)
implementation(libs.serialization.json)
implementation(libs.ktor.client.json)
implementation(libs.ktor.client.logging)
implementation(libs.ktor.client.serialization)
implementation(libs.ktor.client.auth)
implementation(libs.okio.multiplatform)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.serialization.kotlinx)
implementation(libs.okio)
}
}
val commonTest by getting {
dependencies {
implementation(project(":openai-core"))
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
implementation(libs.coroutines.test)
implementation(libs.okio.fakefilesystem)
}
}
Expand All @@ -75,7 +61,6 @@ kotlin {
implementation(kotlin("test-js"))
}
}
val nativeMain by getting
val nativeTest by getting {
dependencies {
implementation(libs.ktor.client.curl)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,22 @@ import com.aallam.openai.client.OpenAIConfig
import com.aallam.openai.client.internal.extension.toKtorLogLevel
import com.aallam.openai.client.internal.extension.toKtorLogger
import io.ktor.client.*
import io.ktor.client.features.*
import io.ktor.client.features.auth.*
import io.ktor.client.features.auth.providers.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.features.logging.*
import io.ktor.client.plugins.*
import io.ktor.client.plugins.auth.*
import io.ktor.client.plugins.auth.providers.*
import io.ktor.client.plugins.logging.*
import io.ktor.client.request.*
import io.ktor.http.*
import io.ktor.serialization.kotlinx.*
import kotlinx.serialization.json.Json

/**
* Default Http Client.
*/
internal fun createHttpClient(config: OpenAIConfig): HttpClient {
return HttpClient {
install(JsonFeature) {
serializer = KotlinxSerializer(JsonLenient)
install(ContentNegotiation) {
register(ContentType.Application.Json, KotlinxSerializationConverter(JsonLenient))
}
install(Logging) {
logger = config.logger.toKtorLogger()
Expand Down Expand Up @@ -49,5 +48,4 @@ internal fun createHttpClient(config: OpenAIConfig): HttpClient {
internal val JsonLenient = Json {
isLenient = true
ignoreUnknownKeys = true
useAlternativeNames = false // TODO: remove after https://github.com/Kotlin/kotlinx.serialization/issues/1450
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.aallam.openai.api.answer.Answer
import com.aallam.openai.api.answer.AnswerRequest
import com.aallam.openai.client.Answers
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*

Expand All @@ -15,9 +16,11 @@ internal class AnswersApi(private val httpClient: HttpClient) : Answers {

@ExperimentalOpenAI
override suspend fun answers(request: AnswerRequest): Answer {
return httpClient.post(path = AnswersPath, body = request) {
return httpClient.post {
url(path = AnswersPath)
setBody(request)
contentType(ContentType.Application.Json)
}
}.body()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.aallam.openai.api.classification.Classification
import com.aallam.openai.api.classification.ClassificationRequest
import com.aallam.openai.client.Classifications
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.http.*

Expand All @@ -15,9 +16,11 @@ internal class ClassificationsApi(private val httpClient: HttpClient) : Classifi

@ExperimentalOpenAI
override suspend fun classifications(request: ClassificationRequest): Classification {
return httpClient.post(path = ClassificationsPath, body = request) {
return httpClient.post {
url(path = ClassificationsPath)
setBody(request)
contentType(ContentType.Application.Json)
}
}.body()
}

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.ktor.client.call.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.utils.*
import io.ktor.client.utils.EmptyContent.contentType
import io.ktor.http.*
import io.ktor.utils.io.*
import kotlinx.coroutines.flow.Flow
Expand All @@ -24,29 +25,29 @@ import kotlinx.serialization.decodeFromString
internal class CompletionsApi(private val httpClient: HttpClient) : Completions {

override suspend fun completion(engineId: EngineId, request: CompletionRequest?): TextCompletion {
return httpClient.post(path = "$EnginesPath/$engineId/completions", body = request ?: EmptyContent) {
return httpClient.post {
url(path = "$EnginesPath/$engineId/completions")
setBody(request ?: EmptyContent)
contentType(ContentType.Application.Json)
}
}.body()
}

override fun completions(engineId: EngineId, request: CompletionRequest?): Flow<TextCompletion> {
return flow {
httpClient.post<HttpStatement>(
path = "$EnginesPath/$engineId/completions",
body = request.toStreamRequest()
) {
val response = httpClient.post {
url(path = "$EnginesPath/$engineId/completions")
setBody(request.toStreamRequest())
contentType(ContentType.Application.Json)
}.execute { response ->
val readChannel = response.receive<ByteReadChannel>()
while (!readChannel.isClosedForRead) {
val line = readChannel.readUTF8Line() ?: ""
val value: TextCompletion = when {
line.startsWith(StreamEndToken) -> break
line.startsWith(StreamPrefix) -> JsonLenient.decodeFromString(line.removePrefix(StreamPrefix))
else -> continue
}
emit(value)
}
val readChannel = response.body<ByteReadChannel>()
while (!readChannel.isClosedForRead) {
val line = readChannel.readUTF8Line() ?: ""
val value: TextCompletion = when {
line.startsWith(StreamEndToken) -> break
line.startsWith(StreamPrefix) -> JsonLenient.decodeFromString(line.removePrefix(StreamPrefix))
else -> continue
}
emit(value)
}
}
}
Expand Down
Loading

0 comments on commit 4468e12

Please sign in to comment.