Skip to content

Commit

Permalink
Merge pull request #5 from PatilShreyas/v0.2.1-kmp
Browse files Browse the repository at this point in the history
Sync with google/generative-ai-android v0.2.1
  • Loading branch information
PatilShreyas committed Feb 29, 2024
2 parents 29e8f6b + bc6e06d commit bd50eb5
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 10 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ The versioning scheme is of the form `X-Y` where:

X is the _Generative AI Android SDK_ version that is being tracked.
Y is the _Multiplatform SDK_ version.
For example, if _Generative AI Android SDK_ is on `0.2.0` and _Multiplatform SDK_ is on `1.0.0`, the artifact for a release will be `dev.shreyaspatil.generativeai:generativeai-google:0.2.0-1.0.0`.
For example, if _Generative AI Android SDK_ is on `0.2.1` and _Multiplatform SDK_ is on `1.0.0`, the artifact for a release will be `dev.shreyaspatil.generativeai:generativeai-google:0.2.1-1.0.0`.

## Try sample app

Expand Down
2 changes: 1 addition & 1 deletion generativeai/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
SONATYPE_HOST=DEFAULT
RELEASE_SIGNING_ENABLED=true
GROUP=dev.shreyaspatil.generativeai
VERSION_NAME=0.2.0-1.0.0
VERSION_NAME=0.2.1-1.0.0

POM_ARTIFACT_ID=generativeai-google
POM_NAME=Google Generative AI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class Chat(private val model: GenerativeModel, val history: MutableList<Content>
* @throws InvalidStateException if the [Chat] instance has an active request.
*/
suspend fun sendMessage(prompt: String): GenerateContentResponse {
val content = content("user") { text(prompt) }
val content = content { text(prompt) }
return sendMessage(content)
}

Expand All @@ -83,7 +83,7 @@ class Chat(private val model: GenerativeModel, val history: MutableList<Content>
* @throws InvalidStateException if the [Chat] instance has an active request.
*/
suspend fun sendMessage(prompt: PlatformImage): GenerateContentResponse {
val content = content("user") { image(prompt) }
val content = content { image(prompt) }
return sendMessage(content)
}

Expand Down Expand Up @@ -149,7 +149,7 @@ class Chat(private val model: GenerativeModel, val history: MutableList<Content>
* @throws InvalidStateException if the [Chat] instance has an active request.
*/
fun sendMessageStream(prompt: String): Flow<GenerateContentResponse> {
val content = content("user") { text(prompt) }
val content = content { text(prompt) }
return sendMessageStream(content)
}

Expand All @@ -161,7 +161,7 @@ class Chat(private val model: GenerativeModel, val history: MutableList<Content>
* @throws InvalidStateException if the [Chat] instance has an active request.
*/
fun sendMessageStream(prompt: PlatformImage): Flow<GenerateContentResponse> {
val content = content("user") { image(prompt) }
val content = content { image(prompt) }
return sendMessageStream(content)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
package dev.shreyaspatil.ai.client.generativeai.internal.api

import dev.shreyaspatil.ai.client.generativeai.internal.util.decodeToFlow
import dev.shreyaspatil.ai.client.generativeai.type.InvalidAPIKeyException
import dev.shreyaspatil.ai.client.generativeai.type.ServerException
import dev.shreyaspatil.ai.client.generativeai.type.UnsupportedUserLocationException
import io.ktor.client.HttpClient
import io.ktor.client.HttpClientConfig
import io.ktor.client.call.body
Expand Down Expand Up @@ -179,7 +181,13 @@ private suspend fun validateResponse(response: HttpResponse) {
} catch (e: Throwable) {
"Unexpected Response:\n$text"
}

if (message.contains("API key not valid")) {
throw InvalidAPIKeyException(message)
}
// TODO (b/325117891): Use a better method than string matching.
if (message == "User location is not supported for the API use.") {
throw UnsupportedUserLocationException()
}
throw ServerException(message)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ internal enum class HarmCategory(override val serialName: String) : Serializable

typealias Base64 = String

@Serializable internal data class Content(val role: String? = null, val parts: List<Part>)
@Serializable internal data class Content(val role: String? = "user", val parts: List<Part>)

@Serializable(PartSerializer::class)
internal sealed interface Part
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ class SerializationException(message: String, cause: Throwable? = null) :
class ServerException(message: String, cause: Throwable? = null) :
GoogleGenerativeAIException(message, cause)

/** The server responded that the API Key is no valid. */
class InvalidAPIKeyException(message: String, cause: Throwable? = null) :
GoogleGenerativeAIException(message, cause)

/**
* A request was blocked for some reason.
*
Expand All @@ -70,6 +74,16 @@ class PromptBlockedException(val response: GenerateContentResponse, cause: Throw
cause,
)

/**
* The user's location (region) is not supported by the API.
*
* See the Google documentation for a
* [list of regions](https://ai.google.dev/available_regions#available_regions) (countries and
* territories) where the API is available.
*/
class UnsupportedUserLocationException(cause: Throwable? = null) :
GoogleGenerativeAIException("User location is not supported for the API use.", cause)

/**
* Some form of state occurred that shouldn't have.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package dev.shreyaspatil.ai.client.generativeai
import dev.shreyaspatil.ai.client.generativeai.type.BlockReason
import dev.shreyaspatil.ai.client.generativeai.type.FinishReason
import dev.shreyaspatil.ai.client.generativeai.type.HarmCategory
import dev.shreyaspatil.ai.client.generativeai.type.InvalidAPIKeyException
import dev.shreyaspatil.ai.client.generativeai.type.PromptBlockedException
import dev.shreyaspatil.ai.client.generativeai.type.ResponseStoppedException
import dev.shreyaspatil.ai.client.generativeai.type.SerializationException
Expand Down Expand Up @@ -172,6 +173,6 @@ internal class StreamingSnapshotTests {
goldenStreamingFile("failure-api-key.txt", HttpStatusCode.BadRequest) {
val responses = model.generateContentStream()

withTimeout(testTimeout) { shouldThrow<ServerException> { responses.collect() } }
withTimeout(testTimeout) { shouldThrow<InvalidAPIKeyException> { responses.collect() } }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ package dev.shreyaspatil.ai.client.generativeai
import dev.shreyaspatil.ai.client.generativeai.type.BlockReason
import dev.shreyaspatil.ai.client.generativeai.type.FinishReason
import dev.shreyaspatil.ai.client.generativeai.type.HarmCategory
import dev.shreyaspatil.ai.client.generativeai.type.InvalidAPIKeyException
import dev.shreyaspatil.ai.client.generativeai.type.PromptBlockedException
import dev.shreyaspatil.ai.client.generativeai.type.ResponseStoppedException
import dev.shreyaspatil.ai.client.generativeai.type.SerializationException
import dev.shreyaspatil.ai.client.generativeai.type.ServerException
import dev.shreyaspatil.ai.client.generativeai.type.UnsupportedUserLocationException
import dev.shreyaspatil.ai.client.generativeai.util.goldenUnaryFile
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.should
Expand Down Expand Up @@ -93,6 +95,14 @@ internal class UnarySnapshotTests {
withTimeout(testTimeout) { shouldThrow<ServerException> { model.generateContent() } }
}

@Test
fun `user location error`() =
goldenUnaryFile("failure-unsupported-user-location.json", HttpStatusCode.PreconditionFailed) {
withTimeout(testTimeout) {
shouldThrow<UnsupportedUserLocationException> { model.generateContent() }
}
}

@Test
fun `stopped for safety`() =
goldenUnaryFile("failure-finish-reason-safety.json") {
Expand Down Expand Up @@ -128,7 +138,7 @@ internal class UnarySnapshotTests {
@Test
fun `invalid api key`() =
goldenUnaryFile("failure-api-key.json", HttpStatusCode.BadRequest) {
withTimeout(testTimeout) { shouldThrow<ServerException> { model.generateContent() } }
withTimeout(testTimeout) { shouldThrow<InvalidAPIKeyException> { model.generateContent() } }
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"error": {
"code": 400,
"message": "User location is not supported for the API use.",
"status": "FAILED_PRECONDITION",
"details": [
{
"@type": "type.googleapis.com/google.rpc.DebugInfo",
"detail": "[ORIGINAL ERROR] generic::failed_precondition: User location is not supported for the API use. [google.rpc.error_details_ext] { message: \"User location is not supported for the API use.\" }"
}
]
}
}

0 comments on commit bd50eb5

Please sign in to comment.