From 47135cf17ece1c3a7cd99b3ffdf62be4e85034e8 Mon Sep 17 00:00:00 2001 From: David Motsonashvili Date: Mon, 25 Mar 2024 17:39:46 +0000 Subject: [PATCH] added quota exceeded exception (#93) Co-authored-by: David Motsonashvili Co-authored-by: Rodrigo Lazo --- .../generativeai/common/APIController.kt | 3 ++ .../client/generativeai/common/Exceptions.kt | 10 ++++-- .../generativeai/common/UnarySnapshotTests.kt | 10 ++++++ .../unary/failure-quota-exceeded.json | 31 +++++++++++++++++++ .../ai/client/generativeai/type/Exceptions.kt | 14 ++++++--- 5 files changed, 60 insertions(+), 8 deletions(-) create mode 100644 common/src/test/resources/golden-files/unary/failure-quota-exceeded.json diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt b/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt index 757554ce..e9cbae41 100644 --- a/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt +++ b/common/src/main/kotlin/com/google/ai/client/generativeai/common/APIController.kt @@ -198,6 +198,9 @@ private suspend fun validateResponse(response: HttpResponse) { if (message == "User location is not supported for the API use.") { throw UnsupportedUserLocationException() } + if (message.contains("quota")) { + throw QuotaExceededException(message) + } throw ServerException(message) } diff --git a/common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt b/common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt index d28ea2ff..491b8cb4 100644 --- a/common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt +++ b/common/src/main/kotlin/com/google/ai/client/generativeai/common/Exceptions.kt @@ -37,7 +37,7 @@ sealed class GoogleGenerativeAIException(message: String, cause: Throwable? = nu is kotlinx.serialization.SerializationException -> SerializationException( "Something went wrong while trying to deserialize a response from the server.", - cause + cause, ) is TimeoutCancellationException -> RequestTimeoutException("The request failed to complete in the allotted time.") @@ -68,7 +68,7 @@ class InvalidAPIKeyException(message: String, cause: Throwable? = null) : class PromptBlockedException(val response: GenerateContentResponse, cause: Throwable? = null) : GoogleGenerativeAIException( "Prompt was blocked: ${response.promptFeedback?.blockReason?.name}", - cause + cause, ) /** @@ -97,7 +97,7 @@ class InvalidStateException(message: String, cause: Throwable? = null) : class ResponseStoppedException(val response: GenerateContentResponse, cause: Throwable? = null) : GoogleGenerativeAIException( "Content generation stopped. Reason: ${response.candidates?.first()?.finishReason?.name}", - cause + cause, ) /** @@ -108,6 +108,10 @@ class ResponseStoppedException(val response: GenerateContentResponse, cause: Thr class RequestTimeoutException(message: String, cause: Throwable? = null) : GoogleGenerativeAIException(message, cause) +/** The quota for this API key is depleted, retry this request at a later time. */ +class QuotaExceededException(message: String, cause: Throwable? = null) : + GoogleGenerativeAIException(message, cause) + /** Catch all case for exceptions not explicitly expected. */ class UnknownException(message: String, cause: Throwable? = null) : GoogleGenerativeAIException(message, cause) diff --git a/common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt b/common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt index ac4b9750..606986c4 100644 --- a/common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt +++ b/common/src/test/java/com/google/ai/client/generativeai/common/UnarySnapshotTests.kt @@ -187,6 +187,16 @@ internal class UnarySnapshotTests { } } + @Test + fun `quota exceeded`() = + goldenUnaryFile("failure-quota-exceeded.json", HttpStatusCode.BadRequest) { + withTimeout(testTimeout) { + shouldThrow { + apiController.generateContent(textGenerateContentRequest("prompt")) + } + } + } + @Test fun `image rejected`() = goldenUnaryFile("failure-image-rejected.json", HttpStatusCode.BadRequest) { diff --git a/common/src/test/resources/golden-files/unary/failure-quota-exceeded.json b/common/src/test/resources/golden-files/unary/failure-quota-exceeded.json new file mode 100644 index 00000000..fc438f7b --- /dev/null +++ b/common/src/test/resources/golden-files/unary/failure-quota-exceeded.json @@ -0,0 +1,31 @@ +{ + "error": { + "code": 429, + "message": "Quota exceeded for quota metric 'Generate Content API requests per minute' and limit 'GenerateContent request limit per minute for a region' of service 'generativelanguage.googleapis.com' for consumer 'project_number:348715329010'.", + "status": "RESOURCE_EXHAUSTED", + "details": [ + { + "@type": "type.googleapis.com/google.rpc.ErrorInfo", + "reason": "RATE_LIMIT_EXCEEDED", + "domain": "googleapis.com", + "metadata": { + "service": "generativelanguage.googleapis.com", + "consumer": "projects/348715329010", + "quota_limit_value": "0", + "quota_limit": "GenerateContentRequestsPerMinutePerProjectPerRegion", + "quota_location": "us-east2", + "quota_metric": "generativelanguage.googleapis.com/generate_content_requests" + } + }, + { + "@type": "type.googleapis.com/google.rpc.Help", + "links": [ + { + "description": "Request a higher quota limit.", + "url": "https://cloud.google.com/docs/quota#requesting_higher_quota" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/generativeai/src/main/java/com/google/ai/client/generativeai/type/Exceptions.kt b/generativeai/src/main/java/com/google/ai/client/generativeai/type/Exceptions.kt index 5cfc197e..cc64c6c6 100644 --- a/generativeai/src/main/java/com/google/ai/client/generativeai/type/Exceptions.kt +++ b/generativeai/src/main/java/com/google/ai/client/generativeai/type/Exceptions.kt @@ -42,9 +42,7 @@ sealed class GoogleGenerativeAIException(message: String, cause: Throwable? = nu is com.google.ai.client.generativeai.common.ServerException -> ServerException(cause.message ?: "", cause.cause) is com.google.ai.client.generativeai.common.InvalidAPIKeyException -> - InvalidAPIKeyException( - cause.message ?: "", - ) + InvalidAPIKeyException(cause.message ?: "") is com.google.ai.client.generativeai.common.PromptBlockedException -> PromptBlockedException(cause.response.toPublic(), cause.cause) is com.google.ai.client.generativeai.common.UnsupportedUserLocationException -> @@ -57,6 +55,8 @@ sealed class GoogleGenerativeAIException(message: String, cause: Throwable? = nu RequestTimeoutException(cause.message ?: "", cause.cause) is com.google.ai.client.generativeai.common.UnknownException -> UnknownException(cause.message ?: "", cause.cause) + is com.google.ai.client.generativeai.common.QuotaExceededException -> + QuotaExceededException(cause.message ?: "", cause.cause) else -> UnknownException(cause.message ?: "", cause) } is TimeoutCancellationException -> @@ -89,7 +89,7 @@ class InvalidAPIKeyException(message: String, cause: Throwable? = null) : class PromptBlockedException(val response: GenerateContentResponse, cause: Throwable? = null) : GoogleGenerativeAIException( "Prompt was blocked: ${response.promptFeedback?.blockReason?.name}", - cause + cause, ) /** @@ -119,7 +119,7 @@ class InvalidStateException(message: String, cause: Throwable? = null) : class ResponseStoppedException(val response: GenerateContentResponse, cause: Throwable? = null) : GoogleGenerativeAIException( "Content generation stopped. Reason: ${response.candidates.first().finishReason?.name}", - cause + cause, ) /** @@ -130,6 +130,10 @@ class ResponseStoppedException(val response: GenerateContentResponse, cause: Thr class RequestTimeoutException(message: String, cause: Throwable? = null) : GoogleGenerativeAIException(message, cause) +/** The quota for this API key is depleted, retry this request at a later time. */ +class QuotaExceededException(message: String, cause: Throwable? = null) : + GoogleGenerativeAIException(message, cause) + /** Catch all case for exceptions not explicitly expected. */ class UnknownException(message: String, cause: Throwable? = null) : GoogleGenerativeAIException(message, cause)