diff --git a/.gitignore b/.gitignore index 347e252e..b8ed8387 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,9 @@ .gradle/ build/ +# KMP files +.kotlin/ + # Local configuration file (sdk path, etc) local.properties diff --git a/build.gradle.kts b/build.gradle.kts index eb02553e..726b27b9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -18,6 +18,7 @@ plugins { id("com.android.library") version "8.1.3" apply false id("org.jetbrains.dokka") version "1.8.20" apply false kotlin("android") version "1.8.22" apply false + kotlin("multiplatform") version "2.0.0" apply false kotlin("plugin.serialization") version "1.8.22" apply false id("com.ncorti.ktfmt.gradle") version "0.18.0" apply false id("license-plugin") diff --git a/common/build.gradle.kts b/common/build.gradle.kts index bd02f5fa..6ee0cb3c 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -14,13 +14,19 @@ * limitations under the License. */ +@file:Suppress("UnstableApiUsage") +@file:OptIn(ExperimentalKotlinGradlePluginApi::class) + +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + plugins { - id("com.android.library") + id("com.android.kotlin.multiplatform.library") // version "8.6.0-alpha02" + kotlin("multiplatform") version "2.0.0" id("maven-publish") - id("com.ncorti.ktfmt.gradle") + id("com.ncorti.ktfmt.gradle") version "0.18.0" id("changelog-plugin") id("release-plugin") - kotlin("android") kotlin("plugin.serialization") } @@ -28,75 +34,54 @@ ktfmt { googleStyle() } -android { - namespace = "com.google.ai.client.generativeai.common" - compileSdk = 34 - - buildFeatures.buildConfig = true - - defaultConfig { +kotlin { + val ktorVersion = "2.3.2" + androidLibrary { + compileSdk = 34 minSdk = 21 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - consumerProguardFiles("consumer-rules.pro") - - buildConfigField("String", "VERSION_NAME", "\"${project.version.toString()}\"") - } - - publishing { - singleVariant("release") { - withSourcesJar() + namespace = "com.google.ai.client.generativeai.common" + optimization { + minify = false + consumerKeepRules.file("consumer-rules.pro") + consumerKeepRules.publish = true } - } - - buildTypes { - release { - isMinifyEnabled = false - proguardFiles( - getDefaultProguardFile("proguard-android-optimize.txt"), - "proguard-rules.pro" - ) + withAndroidTestOnJvm { + isReturnDefaultValues = true } } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 - } - kotlinOptions { - jvmTarget = "17" + jvmToolchain(17) + compilerOptions { + apiVersion.set(KotlinVersion.KOTLIN_1_8) + languageVersion.set(KotlinVersion.KOTLIN_1_8) } - testOptions { - unitTests.isReturnDefaultValues = true - } -} - -dependencies { - val ktorVersion = "2.3.2" - - implementation("io.ktor:ktor-client-okhttp:$ktorVersion") - implementation("io.ktor:ktor-client-core:$ktorVersion") - implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") - implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") - implementation("io.ktor:ktor-client-logging:$ktorVersion") - - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") - implementation("androidx.core:core-ktx:1.12.0") - implementation("org.slf4j:slf4j-nop:2.0.9") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:1.7.3") - implementation("org.reactivestreams:reactive-streams:1.0.3") + jvm() + sourceSets { + commonMain.dependencies { + implementation("io.ktor:ktor-client-core:$ktorVersion") + implementation("org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22") + implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion") + implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion") + implementation("io.ktor:ktor-client-logging:$ktorVersion") - implementation("com.google.guava:listenablefuture:1.0") - implementation("androidx.concurrent:concurrent-futures:1.2.0-alpha02") - implementation("androidx.concurrent:concurrent-futures-ktx:1.2.0-alpha02") - testImplementation("junit:junit:4.13.2") - testImplementation("io.kotest:kotest-assertions-core:4.0.7") - testImplementation("io.kotest:kotest-assertions-jvm:4.0.7") - testImplementation("io.kotest:kotest-assertions-json:4.0.7") - testImplementation("io.ktor:ktor-client-mock:$ktorVersion") - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1") + implementation("org.slf4j:slf4j-nop:2.0.9") + } + androidMain.dependencies { + implementation("io.ktor:ktor-client-okhttp:$ktorVersion") + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.22") + } + jvmMain.dependencies { + implementation("io.ktor:ktor-client-okhttp:$ktorVersion") + implementation("org.jetbrains.kotlin:kotlin-stdlib:1.8.22") + } + jvmTest.dependencies { + implementation("junit:junit:4.13.2") + implementation("io.kotest:kotest-assertions-core-jvm:4.0.7") + implementation("io.kotest:kotest-assertions-json-jvm:4.0.7") + implementation("io.ktor:ktor-client-mock-jvm:$ktorVersion") + } + } } publishing { @@ -113,9 +98,6 @@ publishing { } } } - afterEvaluate { - from(components["release"]) - } } } repositories { diff --git a/common/gradle.properties b/common/gradle.properties index 59a78457..d8f410ab 100644 --- a/common/gradle.properties +++ b/common/gradle.properties @@ -1 +1,3 @@ version=0.5.0 +kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlin.compiler.runViaBuildToolsApi=true diff --git a/common/src/main/AndroidManifest.xml b/common/src/androidMain/AndroidManifest.xml similarity index 100% rename from common/src/main/AndroidManifest.xml rename to common/src/androidMain/AndroidManifest.xml diff --git a/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/APIController.android.kt b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/APIController.android.kt new file mode 100644 index 00000000..1c53db76 --- /dev/null +++ b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/APIController.android.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.OkHttp + +internal actual fun createHttpClient(): HttpClientEngine = OkHttp.create() diff --git a/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.android.kt b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.android.kt new file mode 100644 index 00000000..59b398eb --- /dev/null +++ b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.android.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +import kotlin.reflect.KClass + +internal actual inline fun KClass.name(): String = java.name diff --git a/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/logging.android.kt b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/logging.android.kt new file mode 100644 index 00000000..7ef76222 --- /dev/null +++ b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/logging.android.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +import android.util.Log + +internal actual fun Error(tag: String, message: String) { + Log.e(tag, message) +} + +internal actual fun Warning(tag: String, message: String) { + Log.w(tag, message) +} diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.android.kt similarity index 51% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt rename to common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.android.kt index 8f681542..5b425f25 100644 --- a/common/src/main/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt +++ b/common/src/androidMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.android.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023 Google LLC + * Copyright 2024 Google LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,19 +16,10 @@ package com.google.ai.client.generativeai.common.util +import com.google.ai.client.generativeai.common.SerializationException +import kotlinx.serialization.SerialName import java.lang.reflect.Field - -/** - * Removes the last character from the [StringBuilder]. - * - * If the StringBuilder is empty, calling this function will throw an [IndexOutOfBoundsException]. - * - * @return The [StringBuilder] used to make the call, for optional chaining. - * @throws IndexOutOfBoundsException if the StringBuilder is empty. - */ -internal fun StringBuilder.removeLast(): StringBuilder = - if (isEmpty()) throw IndexOutOfBoundsException("StringBuilder is empty.") - else deleteCharAt(length - 1) +import kotlin.reflect.KClass /** * A variant of [getAnnotation][Field.getAnnotation] that provides implicit Kotlin support. @@ -39,3 +30,20 @@ internal fun StringBuilder.removeLast(): StringBuilder = * ``` */ internal inline fun Field.getAnnotation() = getAnnotation(T::class.java) + + +/** + * Provides the name to be used in serialization for this enum value. + * + * By default an enum is serialized to its [name][Enum.name], and can be overwritten by providing a + * [SerialName] annotation. + */ +internal actual fun > T.serialName(): String = declaringJavaClass.getField(name).getAnnotation()?.value ?: name + +/** + * Variant of [kotlin.enumValues] that provides support for [KClass] instances of enums. + * + * @throws SerializationException if the class is not a valid enum. Beyond runtime emily magic, this + * shouldn't really be possible. + */ +internal actual fun > KClass.enumValues(): Array = java.enumConstants ?: throw SerializationException("$simpleName is not a valid enum type.") diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/APIController.kt similarity index 95% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/APIController.kt index faed8ee2..c6a712c7 100644 --- a/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt +++ b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/APIController.kt @@ -16,13 +16,14 @@ package com.google.ai.client.generativeai.common -import android.util.Log +// import android.util.Log import com.google.ai.client.generativeai.common.server.FinishReason +import com.google.ai.client.generativeai.common.util.Warning import com.google.ai.client.generativeai.common.util.decodeToFlow +import com.google.ai.client.generativeai.common.util.name import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.engine.HttpClientEngine -import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.plugins.HttpTimeout import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.HttpRequestBuilder @@ -53,6 +54,8 @@ internal val JSON = Json { prettyPrint = false } +internal expect fun createHttpClient(): HttpClientEngine + /** * Backend class for interfacing with the Gemini API. * @@ -81,7 +84,7 @@ internal constructor( requestOptions: RequestOptions, apiClient: String, headerProvider: HeaderProvider? = null, - ) : this(key, model, requestOptions, OkHttp.create(), apiClient, headerProvider) + ) : this(key, model, requestOptions, createHttpClient(), apiClient, headerProvider) private val model = fullModelName(model) @@ -150,7 +153,7 @@ internal constructor( } } } catch (e: TimeoutCancellationException) { - Log.w(TAG, "HeaderProvided timed out without generating headers, ignoring") + Warning(TAG, "HeaderProvided timed out without generating headers, ignoring") } } } @@ -203,7 +206,7 @@ internal constructor( } companion object { - private val TAG = APIController::class.java.simpleName + private val TAG = APIController::class.name() } } diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/Request.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Request.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/Request.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Request.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/RequestOptions.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/RequestOptions.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/RequestOptions.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/RequestOptions.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/Response.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Response.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/Response.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/Response.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/client/Types.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/client/Types.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/client/Types.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/client/Types.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/server/Types.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/server/Types.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/server/Types.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/server/Types.kt diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/shared/Types.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/shared/Types.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/shared/Types.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/shared/Types.kt diff --git a/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt new file mode 100644 index 00000000..fd251275 --- /dev/null +++ b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +import kotlin.reflect.KClass + +internal expect inline fun KClass.name(): String diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/util/ktor.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/ktor.kt similarity index 100% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/util/ktor.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/ktor.kt diff --git a/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/logging.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/logging.kt new file mode 100644 index 00000000..e2eebd97 --- /dev/null +++ b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/logging.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +internal expect fun Error(tag: String, message: String) + +internal expect fun Warning(tag: String, message: String) diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt similarity index 87% rename from common/src/main/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt rename to common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt index 65040487..abb88059 100644 --- a/common/src/main/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt +++ b/common/src/commonMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.kt @@ -16,7 +16,7 @@ package com.google.ai.client.generativeai.common.util -import android.util.Log +// import android.util.Log import com.google.ai.client.generativeai.common.SerializationException import kotlin.reflect.KClass import kotlinx.serialization.KSerializer @@ -41,12 +41,12 @@ class FirstOrdinalSerializer>(private val enumClass: KClass) : KS val name = decoder.decodeString() val values = enumClass.enumValues() - return values.firstOrNull { it.serialName == name } + return values.firstOrNull { it.serialName() == name } ?: values.first().also { printWarning(name) } } private fun printWarning(name: String) { - Log.e( + Error( "FirstOrdinalSerializer", """ |Unknown enum value found: $name" @@ -60,7 +60,7 @@ class FirstOrdinalSerializer>(private val enumClass: KClass) : KS } override fun serialize(encoder: Encoder, value: T) { - encoder.encodeString(value.serialName) + encoder.encodeString(value.serialName()) } } @@ -70,8 +70,7 @@ class FirstOrdinalSerializer>(private val enumClass: KClass) : KS * By default an enum is serialized to its [name][Enum.name], and can be overwritten by providing a * [SerialName] annotation. */ -val > T.serialName: String - get() = declaringJavaClass.getField(name).getAnnotation()?.value ?: name +internal expect fun > T.serialName(): String /** * Variant of [kotlin.enumValues] that provides support for [KClass] instances of enums. @@ -79,5 +78,4 @@ val > T.serialName: String * @throws SerializationException if the class is not a valid enum. Beyond runtime emily magic, this * shouldn't really be possible. */ -fun > KClass.enumValues(): Array = - java.enumConstants ?: throw SerializationException("$simpleName is not a valid enum type.") +internal expect fun > KClass.enumValues(): Array diff --git a/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/APIController.jvm.kt b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/APIController.jvm.kt new file mode 100644 index 00000000..1c53db76 --- /dev/null +++ b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/APIController.jvm.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common + +import io.ktor.client.engine.HttpClientEngine +import io.ktor.client.engine.okhttp.OkHttp + +internal actual fun createHttpClient(): HttpClientEngine = OkHttp.create() diff --git a/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.jvm.kt b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.jvm.kt new file mode 100644 index 00000000..59b398eb --- /dev/null +++ b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/kotlin.jvm.kt @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +import kotlin.reflect.KClass + +internal actual inline fun KClass.name(): String = java.name diff --git a/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/logging.jvm.kt b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/logging.jvm.kt new file mode 100644 index 00000000..6590c6d3 --- /dev/null +++ b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/logging.jvm.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +internal actual fun Error(tag: String, message: String) { + System.err.println("[$tag]: $message") +} + +internal actual fun Warning(tag: String, message: String) { + println("[$tag]: $message") +} diff --git a/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.jvm.kt b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.jvm.kt new file mode 100644 index 00000000..7edf1435 --- /dev/null +++ b/common/src/jvmMain/kotlin/com/google/ai/client/generativeai/common/util/serialization.jvm.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.ai.client.generativeai.common.util + +import com.google.ai.client.generativeai.common.SerializationException +import java.lang.reflect.Field +import kotlin.reflect.KClass +import kotlinx.serialization.SerialName + +/** + * A variant of [getAnnotation][Field.getAnnotation] that provides implicit Kotlin support. + * + * Syntax sugar for: + * ``` + * getAnnotation(T::class.java) + * ``` + */ +internal inline fun Field.getAnnotation() = getAnnotation(T::class.java) + +/** + * Provides the name to be used in serialization for this enum value. + * + * By default an enum is serialized to its [name][Enum.name], and can be overwritten by providing a + * [SerialName] annotation. + */ +internal actual fun > T.serialName(): String = + declaringJavaClass.getField(name).getAnnotation()?.value ?: name + +/** + * Variant of [kotlin.enumValues] that provides support for [KClass] instances of enums. + * + * @throws SerializationException if the class is not a valid enum. Beyond runtime emily magic, this + * shouldn't really be possible. + */ +internal actual fun > KClass.enumValues(): Array = + java.enumConstants ?: throw SerializationException("$simpleName is not a valid enum type.") diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/APIControllerTests.kt b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/APIControllerTests.kt similarity index 99% rename from common/src/test/java/com/google/ai/client/generativeai/common/APIControllerTests.kt rename to common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/APIControllerTests.kt index 31171ece..c4fa5e2e 100644 --- a/common/src/test/java/com/google/ai/client/generativeai/common/APIControllerTests.kt +++ b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/APIControllerTests.kt @@ -94,7 +94,7 @@ internal class RequestFormatTests { "gemini-pro-1.0", RequestOptions(), mockEngine, - "genai-android/${BuildConfig.VERSION_NAME}", + "genai-android/1.0.0", null, ) diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/StreamingSnapshotTests.kt b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/StreamingSnapshotTests.kt similarity index 100% rename from common/src/test/java/com/google/ai/client/generativeai/common/StreamingSnapshotTests.kt rename to common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/StreamingSnapshotTests.kt diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt similarity index 100% rename from common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt rename to common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/util/kotlin.kt b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt similarity index 100% rename from common/src/test/java/com/google/ai/client/generativeai/common/util/kotlin.kt rename to common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/util/kotlin.kt diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/util/tests.kt b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/util/tests.kt similarity index 98% rename from common/src/test/java/com/google/ai/client/generativeai/common/util/tests.kt rename to common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/util/tests.kt index 88599c80..0064a9a5 100644 --- a/common/src/test/java/com/google/ai/client/generativeai/common/util/tests.kt +++ b/common/src/jvmTest/kotlin/com/google/ai/client/generativeai/common/util/tests.kt @@ -183,4 +183,4 @@ internal fun goldenUnaryFile( internal fun loadGoldenFile(path: String): File = loadResourceFile("golden-files/$path") /** Loads a file from the test resources directory. */ -internal fun loadResourceFile(path: String) = File("src/test/resources/$path") +internal fun loadResourceFile(path: String) = File("src/jvmTest/resources/$path") diff --git a/common/src/test/resources/golden-files/streaming/failure-api-key.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-api-key.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-api-key.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-api-key.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-empty-content.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-empty-content.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-empty-content.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-empty-content.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-finish-reason-safety.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-finish-reason-safety.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-finish-reason-safety.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-finish-reason-safety.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-http-error.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-http-error.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-http-error.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-http-error.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-image-rejected.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-image-rejected.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-image-rejected.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-image-rejected.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-prompt-blocked-safety.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-prompt-blocked-safety.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-prompt-blocked-safety.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-prompt-blocked-safety.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-recitation-no-content.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-recitation-no-content.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-recitation-no-content.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-recitation-no-content.txt diff --git a/common/src/test/resources/golden-files/streaming/failure-unknown-model.txt b/common/src/jvmTest/resources/golden-files/streaming/failure-unknown-model.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/failure-unknown-model.txt rename to common/src/jvmTest/resources/golden-files/streaming/failure-unknown-model.txt diff --git a/common/src/test/resources/golden-files/streaming/success-basic-reply-long.txt b/common/src/jvmTest/resources/golden-files/streaming/success-basic-reply-long.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-basic-reply-long.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-basic-reply-long.txt diff --git a/common/src/test/resources/golden-files/streaming/success-basic-reply-short.txt b/common/src/jvmTest/resources/golden-files/streaming/success-basic-reply-short.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-basic-reply-short.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-basic-reply-short.txt diff --git a/common/src/test/resources/golden-files/streaming/success-citations-altname.txt b/common/src/jvmTest/resources/golden-files/streaming/success-citations-altname.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-citations-altname.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-citations-altname.txt diff --git a/common/src/test/resources/golden-files/streaming/success-citations.txt b/common/src/jvmTest/resources/golden-files/streaming/success-citations.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-citations.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-citations.txt diff --git a/common/src/test/resources/golden-files/streaming/success-quotes-escaped.txt b/common/src/jvmTest/resources/golden-files/streaming/success-quotes-escaped.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-quotes-escaped.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-quotes-escaped.txt diff --git a/common/src/test/resources/golden-files/streaming/success-unknown-enum.txt b/common/src/jvmTest/resources/golden-files/streaming/success-unknown-enum.txt similarity index 100% rename from common/src/test/resources/golden-files/streaming/success-unknown-enum.txt rename to common/src/jvmTest/resources/golden-files/streaming/success-unknown-enum.txt diff --git a/common/src/test/resources/golden-files/unary/failure-api-key.json b/common/src/jvmTest/resources/golden-files/unary/failure-api-key.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-api-key.json rename to common/src/jvmTest/resources/golden-files/unary/failure-api-key.json diff --git a/common/src/test/resources/golden-files/unary/failure-empty-content.json b/common/src/jvmTest/resources/golden-files/unary/failure-empty-content.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-empty-content.json rename to common/src/jvmTest/resources/golden-files/unary/failure-empty-content.json diff --git a/common/src/test/resources/golden-files/unary/failure-finish-reason-safety.json b/common/src/jvmTest/resources/golden-files/unary/failure-finish-reason-safety.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-finish-reason-safety.json rename to common/src/jvmTest/resources/golden-files/unary/failure-finish-reason-safety.json diff --git a/common/src/test/resources/golden-files/unary/failure-http-error.json b/common/src/jvmTest/resources/golden-files/unary/failure-http-error.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-http-error.json rename to common/src/jvmTest/resources/golden-files/unary/failure-http-error.json diff --git a/common/src/test/resources/golden-files/unary/failure-image-rejected.json b/common/src/jvmTest/resources/golden-files/unary/failure-image-rejected.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-image-rejected.json rename to common/src/jvmTest/resources/golden-files/unary/failure-image-rejected.json diff --git a/common/src/test/resources/golden-files/unary/failure-invalid-response.json b/common/src/jvmTest/resources/golden-files/unary/failure-invalid-response.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-invalid-response.json rename to common/src/jvmTest/resources/golden-files/unary/failure-invalid-response.json diff --git a/common/src/test/resources/golden-files/unary/failure-malformed-content.json b/common/src/jvmTest/resources/golden-files/unary/failure-malformed-content.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-malformed-content.json rename to common/src/jvmTest/resources/golden-files/unary/failure-malformed-content.json diff --git a/common/src/test/resources/golden-files/unary/failure-prompt-blocked-safety.json b/common/src/jvmTest/resources/golden-files/unary/failure-prompt-blocked-safety.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-prompt-blocked-safety.json rename to common/src/jvmTest/resources/golden-files/unary/failure-prompt-blocked-safety.json diff --git a/common/src/test/resources/golden-files/unary/failure-quota-exceeded.json b/common/src/jvmTest/resources/golden-files/unary/failure-quota-exceeded.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-quota-exceeded.json rename to common/src/jvmTest/resources/golden-files/unary/failure-quota-exceeded.json diff --git a/common/src/test/resources/golden-files/unary/failure-service-disabled.json b/common/src/jvmTest/resources/golden-files/unary/failure-service-disabled.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-service-disabled.json rename to common/src/jvmTest/resources/golden-files/unary/failure-service-disabled.json diff --git a/common/src/test/resources/golden-files/unary/failure-unknown-model.json b/common/src/jvmTest/resources/golden-files/unary/failure-unknown-model.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-unknown-model.json rename to common/src/jvmTest/resources/golden-files/unary/failure-unknown-model.json diff --git a/common/src/test/resources/golden-files/unary/failure-unsupported-user-location.json b/common/src/jvmTest/resources/golden-files/unary/failure-unsupported-user-location.json similarity index 100% rename from common/src/test/resources/golden-files/unary/failure-unsupported-user-location.json rename to common/src/jvmTest/resources/golden-files/unary/failure-unsupported-user-location.json diff --git a/common/src/test/resources/golden-files/unary/success-basic-reply-long.json b/common/src/jvmTest/resources/golden-files/unary/success-basic-reply-long.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-basic-reply-long.json rename to common/src/jvmTest/resources/golden-files/unary/success-basic-reply-long.json diff --git a/common/src/test/resources/golden-files/unary/success-basic-reply-short.json b/common/src/jvmTest/resources/golden-files/unary/success-basic-reply-short.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-basic-reply-short.json rename to common/src/jvmTest/resources/golden-files/unary/success-basic-reply-short.json diff --git a/common/src/test/resources/golden-files/unary/success-citations-altname.json b/common/src/jvmTest/resources/golden-files/unary/success-citations-altname.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-citations-altname.json rename to common/src/jvmTest/resources/golden-files/unary/success-citations-altname.json diff --git a/common/src/test/resources/golden-files/unary/success-citations-nolicense.json b/common/src/jvmTest/resources/golden-files/unary/success-citations-nolicense.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-citations-nolicense.json rename to common/src/jvmTest/resources/golden-files/unary/success-citations-nolicense.json diff --git a/common/src/test/resources/golden-files/unary/success-citations.json b/common/src/jvmTest/resources/golden-files/unary/success-citations.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-citations.json rename to common/src/jvmTest/resources/golden-files/unary/success-citations.json diff --git a/common/src/test/resources/golden-files/unary/success-constraint-decoding-json.json b/common/src/jvmTest/resources/golden-files/unary/success-constraint-decoding-json.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-constraint-decoding-json.json rename to common/src/jvmTest/resources/golden-files/unary/success-constraint-decoding-json.json diff --git a/common/src/test/resources/golden-files/unary/success-including-severity.json b/common/src/jvmTest/resources/golden-files/unary/success-including-severity.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-including-severity.json rename to common/src/jvmTest/resources/golden-files/unary/success-including-severity.json diff --git a/common/src/test/resources/golden-files/unary/success-partial-usage-metadata.json b/common/src/jvmTest/resources/golden-files/unary/success-partial-usage-metadata.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-partial-usage-metadata.json rename to common/src/jvmTest/resources/golden-files/unary/success-partial-usage-metadata.json diff --git a/common/src/test/resources/golden-files/unary/success-quote-reply.json b/common/src/jvmTest/resources/golden-files/unary/success-quote-reply.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-quote-reply.json rename to common/src/jvmTest/resources/golden-files/unary/success-quote-reply.json diff --git a/common/src/test/resources/golden-files/unary/success-unknown-enum.json b/common/src/jvmTest/resources/golden-files/unary/success-unknown-enum.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-unknown-enum.json rename to common/src/jvmTest/resources/golden-files/unary/success-unknown-enum.json diff --git a/common/src/test/resources/golden-files/unary/success-usage-metadata.json b/common/src/jvmTest/resources/golden-files/unary/success-usage-metadata.json similarity index 100% rename from common/src/test/resources/golden-files/unary/success-usage-metadata.json rename to common/src/jvmTest/resources/golden-files/unary/success-usage-metadata.json diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 95644b24..461c3e99 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Nov 06 13:03:44 PST 2023 +#Tue May 21 10:49:10 CDT 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/plugins/build.gradle.kts b/plugins/build.gradle.kts index 822a4b26..a7db5367 100644 --- a/plugins/build.gradle.kts +++ b/plugins/build.gradle.kts @@ -15,7 +15,7 @@ */ plugins { - `java-library` +// `java-library` `java-gradle-plugin` `kotlin-dsl` kotlin("jvm") version "1.8.22" @@ -57,7 +57,7 @@ gradlePlugin { } dependencies { - implementation("com.android.tools.build:gradle:8.1.3") + implementation("com.android.tools.build:gradle:8.4.1") implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22") implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0") implementation("org.jetbrains.kotlinx.binary-compatibility-validator:org.jetbrains.kotlinx.binary-compatibility-validator.gradle.plugin:0.13.2") diff --git a/plugins/src/main/java/com/google/gradle/plugins/ApiPlugin.kt b/plugins/src/main/java/com/google/gradle/plugins/ApiPlugin.kt index d94442f3..011347a3 100644 --- a/plugins/src/main/java/com/google/gradle/plugins/ApiPlugin.kt +++ b/plugins/src/main/java/com/google/gradle/plugins/ApiPlugin.kt @@ -17,11 +17,10 @@ package com.google.gradle.plugins import com.google.gradle.tasks.CopyFileTask -import com.google.gradle.util.android import com.google.gradle.util.apply import com.google.gradle.util.file +import com.google.gradle.util.getReleaseClasses import com.google.gradle.util.regularOutputFile -import com.google.gradle.util.release import com.google.gradle.util.tempFile import kotlinx.validation.KotlinApiBuildTask import org.gradle.api.Plugin @@ -61,7 +60,7 @@ abstract class ApiPlugin : Plugin { private fun Project.registerBuildApiTask() = tasks.register("buildApi") { - val classes = provider { android.release.output.classesDirs } + val classes = provider { getReleaseClasses() } inputClassesDirs = files(classes) inputDependencies = files(classes) diff --git a/plugins/src/main/java/com/google/gradle/util/gradle.kt b/plugins/src/main/java/com/google/gradle/util/gradle.kt index ba246f75..2bc4331b 100644 --- a/plugins/src/main/java/com/google/gradle/util/gradle.kt +++ b/plugins/src/main/java/com/google/gradle/util/gradle.kt @@ -16,6 +16,7 @@ package com.google.gradle.util +import com.android.build.api.dsl.KotlinMultiplatformAndroidExtension import com.google.gradle.types.ModuleVersion import java.io.File import org.gradle.api.DefaultTask @@ -29,11 +30,14 @@ import org.gradle.api.provider.Provider import org.gradle.api.tasks.StopActionException import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.findByType import org.gradle.kotlin.dsl.getByType import org.gradle.workers.WorkAction import org.gradle.workers.WorkParameters import org.gradle.workers.WorkQueue import org.jetbrains.kotlin.gradle.dsl.KotlinAndroidProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation import org.jetbrains.kotlin.gradle.utils.provider @@ -153,17 +157,20 @@ fun List>.asSingleProvider(): Provider> { } } -/** The Android extension specific for Kotlin projects within Gradle. */ -val Project.android: KotlinAndroidProjectExtension - get() = extensions.getByType() - /** - * The `"release"` compilation target of a Kotlin Android project. - * - * In non android projects, this would be referred to as the main source set. + * TODO() */ -val KotlinAndroidProjectExtension.release: KotlinJvmAndroidCompilation - get() = target.compilations.getByName("release") +fun Project.getReleaseClasses(): FileCollection { + extensions.findByType()?.let { + return it.target.compilations.getByName("release").output.classesDirs + } + + extensions.findByType()?.let { + return it.targets.getByName("android").compilations.getByName("main").output.classesDirs + } + + throw RuntimeException("Library is missing an Android or KMP plugin") +} /** * Provides a project property as the specified type, or null otherwise. diff --git a/settings.gradle.kts b/settings.gradle.kts index 3f4e53af..189e8ab8 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,6 +20,7 @@ pluginManagement { mavenCentral() gradlePluginPortal() } + includeBuild("./plugins") } dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) @@ -32,4 +33,4 @@ dependencyResolutionManagement { rootProject.name = "generativeai" include(":generativeai") include(":common") -includeBuild("./plugins") +