Skip to content

Commit b9013d5

Browse files
authored
Improve error message for invalid locations (#6428)
If an invalid location is used when creating the `FirebaseVertexAI` object, all requests will fail. The error returned is 404 and HTML content. The message as-is is not easy to read. The new messaging points to the most likely reason for the 404 (invalid location).
1 parent 531f25b commit b9013d5

File tree

3 files changed

+56
-1
lines changed

3 files changed

+56
-1
lines changed

firebase-vertexai/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Unreleased
2+
* [fixed] Improved error message when using an invalid location. (#6428)
23
* [fixed] Fixed issue where Firebase App Check error tokens were unintentionally missing from the requests. (#6409)
34
* [fixed] Clarified in the documentation that `Schema.integer` and `Schema.float` only provide hints to the model. (#6420)
45

firebase-vertexai/src/main/kotlin/com/google/firebase/vertexai/common/APIController.kt

+11
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ import io.ktor.client.statement.bodyAsText
4242
import io.ktor.http.ContentType
4343
import io.ktor.http.HttpStatusCode
4444
import io.ktor.http.contentType
45+
import io.ktor.http.withCharset
4546
import io.ktor.serialization.kotlinx.json.json
47+
import io.ktor.utils.io.charsets.Charset
4648
import kotlin.math.max
4749
import kotlin.time.Duration
4850
import kotlin.time.Duration.Companion.seconds
@@ -225,6 +227,15 @@ internal interface HeaderProvider {
225227

226228
private suspend fun validateResponse(response: HttpResponse) {
227229
if (response.status == HttpStatusCode.OK) return
230+
231+
val htmlContentType = ContentType.Text.Html.withCharset(Charset.forName("utf-8"))
232+
if (response.status == HttpStatusCode.NotFound && response.contentType() == htmlContentType)
233+
throw ServerException(
234+
"""URL not found. Please verify the location used to create the `FirebaseVertexAI` object
235+
| See https://cloud.google.com/vertex-ai/generative-ai/docs/learn/locations#available-regions
236+
| for the list of available locations. Raw response: ${response.bodyAsText()}"""
237+
.trimMargin()
238+
)
228239
val text = response.bodyAsText()
229240
val error =
230241
try {

firebase-vertexai/src/test/java/com/google/firebase/vertexai/GenerativeModelTesting.kt

+44-1
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@ import com.google.firebase.vertexai.common.shared.Content
2424
import com.google.firebase.vertexai.common.shared.TextPart
2525
import com.google.firebase.vertexai.common.util.doBlocking
2626
import com.google.firebase.vertexai.type.RequestOptions
27+
import com.google.firebase.vertexai.type.ServerException
2728
import com.google.firebase.vertexai.type.content
2829
import io.kotest.assertions.json.shouldContainJsonKey
2930
import io.kotest.assertions.json.shouldContainJsonKeyValue
31+
import io.kotest.assertions.throwables.shouldThrow
3032
import io.kotest.matchers.collections.shouldNotBeEmpty
33+
import io.kotest.matchers.string.shouldContain
3134
import io.kotest.matchers.types.shouldBeInstanceOf
3235
import io.ktor.client.engine.mock.MockEngine
3336
import io.ktor.client.engine.mock.respond
@@ -44,7 +47,7 @@ internal class GenerativeModelTesting {
4447
private val TEST_CLIENT_ID = "test"
4548

4649
@Test
47-
fun addition() = doBlocking {
50+
fun `system calling in request`() = doBlocking {
4851
val mockEngine = MockEngine {
4952
respond(
5053
generateContentResponseAsJsonString("text response"),
@@ -84,6 +87,46 @@ internal class GenerativeModelTesting {
8487
}
8588
}
8689

90+
@Test
91+
fun `exception thrown when using invalid location`() = doBlocking {
92+
val mockEngine = MockEngine {
93+
respond(
94+
"""<!DOCTYPE html>
95+
<html lang=en>
96+
<title>Error 404 (Not Found)!!1</title>
97+
"""
98+
.trimIndent(),
99+
HttpStatusCode.NotFound,
100+
headersOf(HttpHeaders.ContentType, "text/html; charset=utf-8")
101+
)
102+
}
103+
104+
val apiController =
105+
APIController(
106+
"super_cool_test_key",
107+
"gemini-1.5-flash",
108+
RequestOptions(),
109+
mockEngine,
110+
TEST_CLIENT_ID,
111+
null,
112+
)
113+
114+
// Creating the
115+
val generativeModel =
116+
GenerativeModel(
117+
"projects/PROJECTID/locations/INVALID_LOCATION/publishers/google/models/gemini-1.5-flash",
118+
controller = apiController
119+
)
120+
121+
val exception =
122+
shouldThrow<ServerException> {
123+
withTimeout(5.seconds) { generativeModel.generateContent("my test prompt") }
124+
}
125+
126+
// Let's not be too strict on the wording to avoid breaking the test unnecessarily.
127+
exception.message shouldContain "location"
128+
}
129+
87130
private fun generateContentResponseAsJsonString(text: String): String {
88131
return JSON.encodeToString(
89132
GenerateContentResponse(listOf(Candidate(Content(parts = listOf(TextPart(text))))))

0 commit comments

Comments
 (0)