diff --git a/firebase-ai/CHANGELOG.md b/firebase-ai/CHANGELOG.md index 616d85ee38c..bc356414fb7 100644 --- a/firebase-ai/CHANGELOG.md +++ b/firebase-ai/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- [changed] Removed redundant internal exception types. (#7475) + # 17.4.0 - [changed] **Breaking Change**: Removed the `candidateCount` option from `LiveGenerationConfig` diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt index 2f529322ffd..62f11319f68 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/ImagenModel.kt @@ -19,8 +19,8 @@ package com.google.firebase.ai import com.google.firebase.FirebaseApp import com.google.firebase.ai.common.APIController import com.google.firebase.ai.common.AppCheckHeaderProvider -import com.google.firebase.ai.common.ContentBlockedException import com.google.firebase.ai.common.GenerateImageRequest +import com.google.firebase.ai.type.ContentBlockedException import com.google.firebase.ai.type.Dimensions import com.google.firebase.ai.type.FirebaseAIException import com.google.firebase.ai.type.ImagenEditMode diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt index b199698aa7b..387f4dabf43 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/APIController.kt @@ -21,16 +21,26 @@ import com.google.firebase.Firebase import com.google.firebase.FirebaseApp import com.google.firebase.ai.common.util.decodeToFlow import com.google.firebase.ai.common.util.fullModelName +import com.google.firebase.ai.type.APINotConfiguredException import com.google.firebase.ai.type.CountTokensResponse import com.google.firebase.ai.type.FinishReason +import com.google.firebase.ai.type.FirebaseAIException import com.google.firebase.ai.type.GRpcErrorResponse import com.google.firebase.ai.type.GenerateContentResponse import com.google.firebase.ai.type.GenerativeBackend import com.google.firebase.ai.type.GenerativeBackendEnum import com.google.firebase.ai.type.ImagenGenerationResponse +import com.google.firebase.ai.type.InvalidAPIKeyException +import com.google.firebase.ai.type.PromptBlockedException import com.google.firebase.ai.type.PublicPreviewAPI +import com.google.firebase.ai.type.QuotaExceededException import com.google.firebase.ai.type.RequestOptions import com.google.firebase.ai.type.Response +import com.google.firebase.ai.type.ResponseStoppedException +import com.google.firebase.ai.type.SerializationException +import com.google.firebase.ai.type.ServerException +import com.google.firebase.ai.type.ServiceDisabledException +import com.google.firebase.ai.type.UnsupportedUserLocationException import com.google.firebase.options import io.ktor.client.HttpClient import io.ktor.client.call.body @@ -149,7 +159,7 @@ internal constructor( .body() .validate() } catch (e: Throwable) { - throw FirebaseCommonAIException.from(e) + throw FirebaseAIException.from(e) } suspend fun generateImage(request: GenerateImageRequest): ImagenGenerationResponse.Internal = @@ -162,7 +172,7 @@ internal constructor( .also { validateResponse(it) } .body() } catch (e: Throwable) { - throw FirebaseCommonAIException.from(e) + throw FirebaseAIException.from(e) } private fun getBidiEndpoint(location: String): String = @@ -187,7 +197,7 @@ internal constructor( applyCommonConfiguration(request) } .map { it.validate() } - .catch { throw FirebaseCommonAIException.from(it) } + .catch { throw FirebaseAIException.from(it) } suspend fun countTokens(request: CountTokensRequest): CountTokensResponse.Internal = try { @@ -199,7 +209,7 @@ internal constructor( .also { validateResponse(it) } .body() } catch (e: Throwable) { - throw FirebaseCommonAIException.from(e) + throw FirebaseAIException.from(e) } private fun HttpRequestBuilder.applyCommonHeaders() { @@ -372,9 +382,9 @@ private fun GenerateContentResponse.Internal.validate() = apply { if ((candidates?.isEmpty() != false) && promptFeedback == null) { throw SerializationException("Error deserializing response, found no valid fields") } - promptFeedback?.blockReason?.let { throw PromptBlockedException(this) } + promptFeedback?.blockReason?.let { throw PromptBlockedException(this.toPublic(), null, null) } candidates ?.mapNotNull { it.finishReason } ?.firstOrNull { it != FinishReason.Internal.STOP } - ?.let { throw ResponseStoppedException(this) } + ?.let { throw ResponseStoppedException(this.toPublic()) } } diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/Exceptions.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/Exceptions.kt deleted file mode 100644 index 9e94f88dcfa..00000000000 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/Exceptions.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.firebase.ai.common - -import com.google.firebase.ai.type.GenerateContentResponse -import io.ktor.serialization.JsonConvertException -import kotlinx.coroutines.TimeoutCancellationException - -/** Parent class for any errors that occur. */ -internal sealed class FirebaseCommonAIException(message: String, cause: Throwable? = null) : - RuntimeException(message, cause) { - companion object { - - /** - * Converts a [Throwable] to a [FirebaseCommonAIException]. - * - * Will populate default messages as expected, and propagate the provided [cause] through the - * resulting exception. - */ - fun from(cause: Throwable): FirebaseCommonAIException = - when (cause) { - is FirebaseCommonAIException -> cause - is JsonConvertException, - is kotlinx.serialization.SerializationException -> - SerializationException( - "Something went wrong while trying to deserialize a response from the server.", - cause, - ) - is TimeoutCancellationException -> - RequestTimeoutException("The request failed to complete in the allotted time.") - else -> UnknownException("Something unexpected happened.", cause) - } - } -} - -/** Something went wrong while trying to deserialize a response from the server. */ -internal class SerializationException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** The server responded with a non 200 response code. */ -internal class ServerException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** The server responded that the API Key is no valid. */ -internal class InvalidAPIKeyException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** - * A request was blocked for some reason. - * - * See the [response's][response] `promptFeedback.blockReason` for more information. - * - * @property response the full server response for the request. - */ -internal class PromptBlockedException -internal constructor( - val response: GenerateContentResponse.Internal?, - cause: Throwable? = null, - message: String? = null, -) : - FirebaseCommonAIException( - "Prompt was blocked: ${response?.promptFeedback?.blockReason?.name?: message}", - cause, - ) { - internal constructor(message: String, cause: Throwable? = null) : this(null, cause, message) -} - -/** - * 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. - */ -internal class UnsupportedUserLocationException(cause: Throwable? = null) : - FirebaseCommonAIException("User location is not supported for the API use.", cause) - -/** - * The Firebase project has not been configured and enabled for the selected API. - * - * For the Gemini Developer API, see - * [steps](https://firebase.google.com/docs/ai-logic/faq-and-troubleshooting?api=dev#error-genai-config-not-found) - */ -internal class APINotConfiguredException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** - * Some form of state occurred that shouldn't have. - * - * Usually indicative of consumer error. - */ -internal class InvalidStateException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** - * A request was stopped during generation for some reason. - * - * @property response the full server response for the request - */ -internal class ResponseStoppedException( - val response: GenerateContentResponse.Internal, - cause: Throwable? = null -) : - FirebaseCommonAIException( - "Content generation stopped. Reason: ${response.candidates?.first()?.finishReason?.name}", - cause, - ) - -/** - * A request took too long to complete. - * - * Usually occurs due to a user specified [timeout][RequestOptions.timeout]. - */ -internal class RequestTimeoutException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** The quota for this API key is depleted, retry this request at a later time. */ -internal class QuotaExceededException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** The service is not enabled for this project. Visit the Firebase Console to enable it. */ -internal class ServiceDisabledException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -/** Catch all case for exceptions not explicitly expected. */ -internal class UnknownException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -internal class ContentBlockedException(message: String, cause: Throwable? = null) : - FirebaseCommonAIException(message, cause) - -internal fun makeMissingCaseException( - source: String, - ordinal: Int -): com.google.firebase.ai.type.SerializationException { - return com.google.firebase.ai.type.SerializationException( - """ - |Missing case for a $source: $ordinal - |This error indicates that one of the `toInternal` conversions needs updating. - |If you're a developer seeing this exception, please file an issue on our GitHub repo: - |https://github.com/firebase/firebase-android-sdk - """ - .trimMargin() - ) -} diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/util/serialization.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/util/serialization.kt index 91490da4126..db0a3745785 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/util/serialization.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/common/util/serialization.kt @@ -17,7 +17,7 @@ package com.google.firebase.ai.common.util import android.util.Log -import com.google.firebase.ai.common.SerializationException +import com.google.firebase.ai.type.SerializationException import kotlin.reflect.KClass import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt index 64bfe43ac85..fed7660d08e 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/Exceptions.kt @@ -17,7 +17,6 @@ package com.google.firebase.ai.type import com.google.firebase.ai.FirebaseAI -import com.google.firebase.ai.common.FirebaseCommonAIException import kotlinx.coroutines.TimeoutCancellationException /** Parent class for any errors that occur from the [FirebaseAI] SDK. */ @@ -35,36 +34,6 @@ internal constructor(message: String, cause: Throwable? = null) : RuntimeExcepti internal fun from(cause: Throwable): FirebaseAIException = when (cause) { is FirebaseAIException -> cause - is FirebaseCommonAIException -> - when (cause) { - is com.google.firebase.ai.common.SerializationException -> - SerializationException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.ServerException -> - ServerException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.InvalidAPIKeyException -> - InvalidAPIKeyException(cause.message ?: "") - is com.google.firebase.ai.common.PromptBlockedException -> - PromptBlockedException(cause.response?.toPublic(), cause.cause) - is com.google.firebase.ai.common.UnsupportedUserLocationException -> - UnsupportedUserLocationException(cause.cause) - is com.google.firebase.ai.common.InvalidStateException -> - InvalidStateException(cause.message ?: "", cause) - is com.google.firebase.ai.common.ResponseStoppedException -> - ResponseStoppedException(cause.response.toPublic(), cause.cause) - is com.google.firebase.ai.common.RequestTimeoutException -> - RequestTimeoutException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.ServiceDisabledException -> - ServiceDisabledException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.UnknownException -> - UnknownException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.ContentBlockedException -> - ContentBlockedException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.QuotaExceededException -> - QuotaExceededException(cause.message ?: "", cause.cause) - is com.google.firebase.ai.common.APINotConfiguredException -> - APINotConfiguredException(cause.message ?: "", cause.cause) - else -> UnknownException(cause.message ?: "", cause) - } is TimeoutCancellationException -> RequestTimeoutException("The request failed to complete in the allotted time.") else -> UnknownException("Something unexpected happened.", cause) @@ -238,3 +207,18 @@ public class PermissionMissingException(message: String, cause: Throwable? = nul /** Catch all case for exceptions not explicitly expected. */ public class UnknownException internal constructor(message: String, cause: Throwable? = null) : FirebaseAIException(message, cause) + +internal fun makeMissingCaseException( + source: String, + ordinal: Int +): com.google.firebase.ai.type.SerializationException { + return com.google.firebase.ai.type.SerializationException( + """ + |Missing case for a $source: $ordinal + |This error indicates that one of the `toInternal` conversions needs updating. + |If you're a developer seeing this exception, please file an issue on our GitHub repo: + |https://github.com/firebase/firebase-android-sdk + """ + .trimMargin() + ) +} diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockMethod.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockMethod.kt index a65d153bc53..c3b2e6c2fce 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockMethod.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockMethod.kt @@ -16,7 +16,6 @@ package com.google.firebase.ai.type -import com.google.firebase.ai.common.makeMissingCaseException import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockThreshold.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockThreshold.kt index 93ebfde5da9..5a68e2d44e5 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockThreshold.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmBlockThreshold.kt @@ -16,7 +16,6 @@ package com.google.firebase.ai.type -import com.google.firebase.ai.common.makeMissingCaseException import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmCategory.kt b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmCategory.kt index 871470f6a25..ef97348b27c 100644 --- a/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmCategory.kt +++ b/firebase-ai/src/main/kotlin/com/google/firebase/ai/type/HarmCategory.kt @@ -16,7 +16,6 @@ package com.google.firebase.ai.type -import com.google.firebase.ai.common.makeMissingCaseException import com.google.firebase.ai.common.util.FirstOrdinalSerializer import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName diff --git a/firebase-ai/src/test/java/com/google/firebase/ai/common/APIControllerTests.kt b/firebase-ai/src/test/java/com/google/firebase/ai/common/APIControllerTests.kt index f87298487ea..f3e818085f7 100644 --- a/firebase-ai/src/test/java/com/google/firebase/ai/common/APIControllerTests.kt +++ b/firebase-ai/src/test/java/com/google/firebase/ai/common/APIControllerTests.kt @@ -28,6 +28,7 @@ import com.google.firebase.ai.type.FunctionCallingConfig import com.google.firebase.ai.type.GoogleSearch import com.google.firebase.ai.type.PublicPreviewAPI import com.google.firebase.ai.type.RequestOptions +import com.google.firebase.ai.type.RequestTimeoutException import com.google.firebase.ai.type.TextPart import com.google.firebase.ai.type.Tool import com.google.firebase.ai.type.ToolConfig